From aee07320421a3a8a620dfd28621e8e707bd566b2 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Wed, 23 Aug 2023 16:12:32 -0700 Subject: [PATCH 01/54] do a single pass in maxByFirstDiffering (#1824) This cuts the runtime for hat allocation in my tests by about 20%. No functional changes. I have not added any tests, but I have confirmed that there are no functional changes using my not-yet-submitted hat golden tests. Part of the reason that the hat golden tests are not yet ready for review is that they are way too slow. ## Checklist - [/] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [/] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [/] I have not broken the cheatsheet --- .../allocateHats/maxByFirstDiffering.test.ts | 40 +++++++++++++++++++ .../util/allocateHats/maxByFirstDiffering.ts | 40 ++++++++++++++++--- 2 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 packages/cursorless-engine/src/util/allocateHats/maxByFirstDiffering.test.ts diff --git a/packages/cursorless-engine/src/util/allocateHats/maxByFirstDiffering.test.ts b/packages/cursorless-engine/src/util/allocateHats/maxByFirstDiffering.test.ts new file mode 100644 index 0000000000..296b199edd --- /dev/null +++ b/packages/cursorless-engine/src/util/allocateHats/maxByFirstDiffering.test.ts @@ -0,0 +1,40 @@ +import * as assert from "assert"; +import { maxByAllowingTies } from "./maxByFirstDiffering"; + +// known good but slow +function goldenMaxByAllowingTies(arr: T[], fn: (item: T) => number): T[] { + const max = Math.max(...arr.map(fn)); + return arr.filter((item) => fn(item) === max); +} + +suite("maxByFirstDiffering", () => { + test("maxByAllowingTies", () => { + const testCases: number[][] = [ + [], + [0], + [1], + [-Infinity], + [+Infinity], + [0, 0], + [0, 1], + [1, 0], + [-Infinity, -Infinity], + [-Infinity, +Infinity], + [+Infinity, -Infinity], + [+Infinity, 0], + [0, +Infinity], + [-Infinity, 0], + [0, -Infinity], + [0, 1, 0, 1], + [1, 0, 1, 0], + [0, 1, 1, 0], + [1, 0, 0, 1], + ]; + + testCases.forEach((testCase) => { + const actual = maxByAllowingTies(testCase, (x) => x); + const expected = goldenMaxByAllowingTies(testCase, (x) => x); + assert.deepStrictEqual(actual, expected); + }); + }); +}); diff --git a/packages/cursorless-engine/src/util/allocateHats/maxByFirstDiffering.ts b/packages/cursorless-engine/src/util/allocateHats/maxByFirstDiffering.ts index e8634c14af..c5bcc2989d 100644 --- a/packages/cursorless-engine/src/util/allocateHats/maxByFirstDiffering.ts +++ b/packages/cursorless-engine/src/util/allocateHats/maxByFirstDiffering.ts @@ -24,15 +24,45 @@ export function maxByFirstDiffering( return undefined; } let remainingValues = arr; - for (const fn of fns) { if (remainingValues.length === 1) { return remainingValues[0]; } - - const max = Math.max(...remainingValues.map(fn)); - remainingValues = remainingValues.filter((item) => fn(item) === max); + remainingValues = maxByAllowingTies(remainingValues, fn); } - return remainingValues[0]; } + +/** + * Given an array of items and a function that returns a number for each item, + * return all items that share the maximum value according to that function. + * @param arr The array to find the max values of + * @param fn A function that returns a number for each item in the array + * @returns All items in the array that share the maximum value + **/ +export function maxByAllowingTies(arr: T[], fn: (item: T) => number): T[] { + // This is equivalent to, but faster than: + // + // const max = Math.max(...arr.map(fn)); + // return arr.filter((item) => fn(item) === max); + // + // It does only a single pass through the array, and allocates no + // intermediate arrays (in the common case). + + // Accumulate all items with the single highest value, + // resetting whenever we find a new highest value. + let best: number = -Infinity; + const keep: T[] = []; + for (const item of arr) { + const value = fn(item); + if (value < best) { + continue; + } + if (value > best) { + best = value; + keep.length = 0; + } + keep.push(item); + } + return keep; +} From f17bdf2f271f86083a998ee148ff634e2c9caa4b Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 24 Aug 2023 00:52:30 +0100 Subject: [PATCH 02/54] Add Cursorless scope visualizer video (#1826) ## Checklist - [ ] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [ ] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [ ] I have not broken the cheatsheet --- docs/user/release-notes/0.27.0.md | 2 +- docs/user/scope-visualizer.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/user/release-notes/0.27.0.md b/docs/user/release-notes/0.27.0.md index 0ed5ef1e6b..639ed8f078 100644 --- a/docs/user/release-notes/0.27.0.md +++ b/docs/user/release-notes/0.27.0.md @@ -24,7 +24,7 @@ But here are some highlights: - `"branch"` ([#1149](https://github.com/cursorless-dev/cursorless/pull/1149)) - `"instance"` ([video 🎬](https://youtu.be/rqWmwcfZ_sw)) ([#1497](https://github.com/cursorless-dev/cursorless/pull/1497)) - `"sentence"` ([video 🎬](https://youtu.be/rdLH2GKJirE)) ([#1595](https://github.com/cursorless-dev/cursorless/pull/1595)) -- Added [the scope visualizer](../scope-visualizer.md) ([#1653](https://github.com/cursorless-dev/cursorless/pull/1653)) to let you visualize scopes live in your editor. Video to follow; stay tuned 😎. But here's a screenshot ![Scope visualizer](../images/visualize-funk.png) +- Added [the scope visualizer](../scope-visualizer.md) ([#1653](https://github.com/cursorless-dev/cursorless/pull/1653)) to let you visualize scopes live in your editor ([video 🎬](https://youtu.be/BbXEzUrf5lU)). And here's a screenshot ![Scope visualizer](../images/visualize-funk.png) - A next-gen inference engine that enables a much more powerful grammar, allowing you to do things like individually targeting a sequence of scopes, eg `"every line air past bat"`. To learn more, see the initial [#1462](https://github.com/cursorless-dev/cursorless/pull/1462), and then the follow-up [#1463](https://github.com/cursorless-dev/cursorless/pull/1463) if you're addicted to 🤯. Note that this grammar only works for our next-gen scopes, which include most generic scopes (`"line"`, `"token"`, `"word"`, `"character"`, `"block"`), as well as some language-specific scopes. We still have a lot of language-specific scopes to upgrade, as the infrastructure to do so is still quite new. Stay tuned - [Stable hats](../hatAssignment.md) ([#1252](https://github.com/cursorless-dev/cursorless/pull/1252)) keep hats from moving around so much while you edit. This change is something you may or may not have consciously noticed, but if you spontaneously experienced a deep feeling of oneness and calm at some point in the past six months, the newly meditative hats might have been the cause 🎩🧘 - Improved support for very large hats. A trend among users has been to slightly increase their line height (eg 1.6) and then use a very large hat size (eg 70) to make the hats more visible. This works great, but adjacent hats would overlap. Now, we automatically detect clashing hats and stop them from growing horizontally if they'd overlap ([#1687](https://github.com/cursorless-dev/cursorless/pull/1687)). @pokey is now running with a line height of 1.6 and a hat size of 70, after getting serious hat-size envy during some user screenshares on discord 👒.![Big hats](big-hats.png) If you want to join the big hat revolution, say `"show settings phrase line height"` to mess with your line height, then `"cursorless settings"` to mess with your hat size diff --git a/docs/user/scope-visualizer.md b/docs/user/scope-visualizer.md index 8535d38840..d4c9561d0b 100644 --- a/docs/user/scope-visualizer.md +++ b/docs/user/scope-visualizer.md @@ -1,6 +1,6 @@ # Scope visualizer -The scope visualizer allows you to visualize Cursorless scopes on your code in real time. It is useful to understand how Cursorless scopes work, and is also useful for Cursorless contributors as they develop new scopes. +The scope visualizer allows you to visualize Cursorless scopes on your code in real time. It is useful to understand how Cursorless scopes work, and is also useful for Cursorless contributors as they develop new scopes. You may find the [scope visualizer intro video](https://youtu.be/BbXEzUrf5lU) helpful. ## Usage From 7a1eac9cc2ab51bf5e728cbb0516907a416e7fa1 Mon Sep 17 00:00:00 2001 From: Jacob Egner Date: Sun, 27 Aug 2023 13:32:18 -0500 Subject: [PATCH 03/54] doc `"drink"` and `"pour"` can do more than lines (#1772) fix #1766 --------- Co-authored-by: Jacob Egner --- cursorless-talon/src/cheatsheet/sections/actions.py | 2 ++ docs/user/README.md | 10 +++++++--- .../src/lib/sampleSpokenFormInfos/defaults.json | 4 ++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/cursorless-talon/src/cheatsheet/sections/actions.py b/cursorless-talon/src/cheatsheet/sections/actions.py index 7bbeeeeaca..37486d4ab1 100644 --- a/cursorless-talon/src/cheatsheet/sections/actions.py +++ b/cursorless-talon/src/cheatsheet/sections/actions.py @@ -35,6 +35,8 @@ def get_actions(): simple_actions, { "callAsFunction": "Call on selection", + "editNewLineAfter": "Edit new line/scope after", + "editNewLineBefore": "Edit new line/scope before", }, ), { diff --git a/docs/user/README.md b/docs/user/README.md index 8b5fec551d..15850b3aca 100644 --- a/docs/user/README.md +++ b/docs/user/README.md @@ -579,9 +579,13 @@ For example: - `"drink "`: Inserts a new line above the target line, and moves the cursor to the newly created line - `"pour "`: Inserts a new line below the target line, and moves the cursor to the newly created line -eg: -`pour blue air` -Insert empty line below the token containing letter 'a' with a blue hat. +eg: `pour blue air` will insert empty line below the token containing letter 'a' with a blue hat and moves the cursor to the new line. + +Note: `"drink"` and `"pour"` are actually a bit more versatile than just lines. +If you use a [syntactic scope](#syntactic-scopes) modifier on the target, then`"drink"` and `"pour"` will do the +appropriate insertions to prepare the text for a new instance of that scope. + +eg: `pour item air` will insert a comma and space after the air item, and place the cursor after the inserted characters. ### Homophones diff --git a/packages/cheatsheet/src/lib/sampleSpokenFormInfos/defaults.json b/packages/cheatsheet/src/lib/sampleSpokenFormInfos/defaults.json index 1d9c4f853d..014a9acc34 100644 --- a/packages/cheatsheet/src/lib/sampleSpokenFormInfos/defaults.json +++ b/packages/cheatsheet/src/lib/sampleSpokenFormInfos/defaults.json @@ -70,7 +70,7 @@ "variations": [ { "spokenForm": "pour ", - "description": "Edit new line after" + "description": "Edit new line/scope after" } ] }, @@ -80,7 +80,7 @@ "variations": [ { "spokenForm": "drink ", - "description": "Edit new line before" + "description": "Edit new line/scope before" } ] }, From 4bc74abf7e9fb618da02f68435aeb44a8fb92c0c Mon Sep 17 00:00:00 2001 From: David Vo Date: Thu, 31 Aug 2023 00:53:06 +1000 Subject: [PATCH 04/54] vscode: Recommend ruff extension (#1833) We added `ruff` in #1375. There's a corresponding VSCode extension by the author, so probably a good idea to use it to surface lints earlier than pre-commit. https://github.com/astral-sh/ruff-vscode ## Checklist - [ ] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [ ] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [ ] I have not broken the cheatsheet --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- .vscode/extensions.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index f39b8b1e4c..8606829da6 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -3,6 +3,7 @@ // for the documentation about the extensions.json format "recommendations": [ "AndreasArvidsson.andreas-talon", + "charliermarsh.ruff", "dbaeumer.vscode-eslint", "esbenp.prettier-vscode", "jrieken.vscode-tree-sitter-query", From 48cedd8aca13eed3ea88f1bc7fa46eddf7094b96 Mon Sep 17 00:00:00 2001 From: David Vo Date: Fri, 1 Sep 2023 23:05:15 +1000 Subject: [PATCH 05/54] tsconfig: Enable isolatedModules (#1829) esbuild recommends enabling this to avoid miscompilation. - https://esbuild.github.io/content-types/#isolated-modules - https://www.typescriptlang.org/tsconfig/isolatedModules.html ## Checklist - [ ] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [ ] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [ ] I have not broken the cheatsheet --- packages/common/src/index.ts | 9 +++++---- tsconfig.base.json | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index 6f087a3b22..f42a47ee7e 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -1,8 +1,8 @@ export * from "./cursorlessCommandIds"; export * from "./testUtil/extractTargetedMarks"; export { default as FakeIDE } from "./ide/fake/FakeIDE"; -export { Message } from "./ide/spy/SpyMessages"; -export { SpyIDERecordedValues } from "./ide/spy/SpyIDE"; +export type { Message } from "./ide/spy/SpyMessages"; +export type { SpyIDERecordedValues } from "./ide/spy/SpyIDE"; export { default as SpyIDE } from "./ide/spy/SpyIDE"; export { HatStability } from "./ide/types/HatStability"; export * from "./util"; @@ -11,8 +11,9 @@ export { getKey, splitKey } from "./util/splitKey"; export { hrtimeBigintToSeconds } from "./util/timeUtils"; export * from "./util/walkSync"; export * from "./util/walkAsync"; -export { Listener, Notifier } from "./util/Notifier"; -export { TokenHatSplittingMode } from "./ide/types/Configuration"; +export { Notifier } from "./util/Notifier"; +export type { Listener } from "./util/Notifier"; +export type { TokenHatSplittingMode } from "./ide/types/Configuration"; export * from "./ide/types/ide.types"; export * from "./ide/types/Capabilities"; export * from "./ide/types/CommandId"; diff --git a/tsconfig.base.json b/tsconfig.base.json index 059d1cb50e..80f180cc27 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -10,6 +10,7 @@ "resolveJsonModule": true, "composite": true, "forceConsistentCasingInFileNames": true, + "isolatedModules": true, "strict": true } } From 52d3eca62bacfd12a974706644b3753bae5f4b98 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Fri, 1 Sep 2023 14:53:24 +0100 Subject: [PATCH 06/54] Release 0.28.0 (#1818) - Rebranding 0.27.0 as 0.28.0 due to botched release process; see https://github.com/cursorless-dev/cursorless/pull/1801#issuecomment-1682628127 - To be merged 1 week after https://github.com/cursorless-dev/cursorless/pull/1801 ## Checklist - [-] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [-] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [-] I have not broken the cheatsheet --- docs/user/release-notes/{0.27.0.md => 0.28.0.md} | 12 ++++++------ packages/cursorless-vscode/package.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) rename docs/user/release-notes/{0.27.0.md => 0.28.0.md} (92%) diff --git a/docs/user/release-notes/0.27.0.md b/docs/user/release-notes/0.28.0.md similarity index 92% rename from docs/user/release-notes/0.27.0.md rename to docs/user/release-notes/0.28.0.md index 639ed8f078..47da0b8523 100644 --- a/docs/user/release-notes/0.27.0.md +++ b/docs/user/release-notes/0.28.0.md @@ -1,9 +1,9 @@ --- -version: "0.27.0" -releaseDate: "2023-08-16" +version: "0.28.0" +releaseDate: "2023-09-01" --- -# Release Notes for 0.27.0 +# Release Notes for 0.28.0 ## Preamble @@ -13,7 +13,7 @@ Given we don't actually have "releases", we're arbitrarily considering everythin ## The subject at hand -Since the start of 2023, we've merged [289 PRs from 9 authors](https://github.com/cursorless-dev/cursorless/pulls?q=is%3Apr+is%3Amerged+merged%3A%3E%3D2023-01-01+sort%3Aupdated-asc) (including a whopping [59](https://github.com/cursorless-dev/cursorless/pulls?q=is%3Apr+is%3Amerged+merged%3A%3E%3D2023-01-01+sort%3Aupdated-asc+author%3AAndreasArvidsson) from [@AndreasArvidsson](https://github.com/sponsors/AndreasArvidsson)!), so there's a lot of ground to cover 😅 +Since the start of 2023, we've merged [306 PRs from 9 authors](https://github.com/cursorless-dev/cursorless/pulls?q=is%3Apr+is%3Amerged+merged%3A2023-01-01..2023-09-01+sort%3Aupdated-asc) (including a whopping [62](https://github.com/cursorless-dev/cursorless/pulls?q=is%3Apr+is%3Amerged+merged%3A2023-01-01..2023-09-01+sort%3Aupdated-asc+author%3AAndreasArvidsson+) from [@AndreasArvidsson](https://github.com/sponsors/AndreasArvidsson)!), so there's a lot of ground to cover 😅 But here are some highlights: @@ -36,13 +36,13 @@ But here are some highlights: ## 🐛 Bug fixes -We fixed [dozens of bugs](https://github.com/cursorless-dev/cursorless/issues?q=is%3Aissue+is%3Aclosed+closed%3A%3E%3D2023-01-01+reason%3Acompleted+sort%3Aupdated-asc+label%3Abug); probably easiest to look at the list. +We fixed [dozens of bugs](https://github.com/cursorless-dev/cursorless/issues?q=is%3Aissue+is%3Aclosed+closed%3A2023-01-01..2023-09-01+reason%3Acompleted+sort%3Aupdated-asc+label%3Abug); probably easiest to look at the list. ## 📜 Documentation - Added a version of the cheatsheet hosted [on our website](https://www.cursorless.org/cheatsheet) so you can show your friends how cool Cursorless is without having to send them a PDF. Note that this version uses the default spoken forms, rather than the ones you've customized. - [@josharian](https://www.youtube.com/@josharian) has made a fantastic series of YouTube videos called [Cursorless by example](https://www.youtube.com/watch?v=2hPwfBCtXws&list=PLbN8ceamGu2c6JrNf83EWyP6K5K77MzVZ). Even Cursorless pros will learn something! -- [Lots of other docs improvements](https://github.com/cursorless-dev/cursorless/pulls?q=is%3Apr+is%3Amerged+merged%3A%3E%3D2023-01-01+sort%3Aupdated-asc+label%3Adocumentation+) +- [Lots of other docs improvements](https://github.com/cursorless-dev/cursorless/pulls?q=is%3Apr+is%3Amerged+merged%3A2023-01-01..2023-09-01+sort%3Aupdated-asc+label%3Adocumentation) (special shout-out to [@jmegner](https://github.com/jmegner)!) ## 🤓 Internal improvements diff --git a/packages/cursorless-vscode/package.json b/packages/cursorless-vscode/package.json index b011aff760..e5ac5803a2 100644 --- a/packages/cursorless-vscode/package.json +++ b/packages/cursorless-vscode/package.json @@ -20,7 +20,7 @@ "description": "Tests" } ], - "version": "0.27.0", + "version": "0.28.0", "publisher": "pokey", "license": "MIT", "repository": { From ae5304ddf3bd602ff2eda089a2fb1d5cddcf4e96 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Fri, 1 Sep 2023 07:17:58 -0700 Subject: [PATCH 07/54] implement non-quadratic version of uniqWith (#1834) When given a comparator, uniqWith is quadratic. Add uniqWithHash that uses an optional hash function to return to approximately linear behavior, assuming that the hash function is good. For targets and selections, Using the contentRange/selection as the hash value seems to be pretty good in practice. I've added "concise" printing versions of positions, ranges, and selections. These are handy as hash functions, but I've also made heavy use of them while investigating hat allocation. They are generally useful. "take every line" on a file containing 3000 lines used to take about 15s on my computer. Now it takes about 1s. That's still not great but it's fast enough that it doesn't appear that Cursorless has hung. I've half a mind to try upstreaming this, or at least sending a documentation fix, because a quadratic API is a big footgun. Fixes #1830 enough for now ## Checklist - [x] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [/] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [/] I have not broken the cheatsheet --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: Pokey Rule <755842+pokey@users.noreply.github.com> --- packages/common/src/types/Position.ts | 12 +++ packages/common/src/types/Range.ts | 12 +++ packages/common/src/types/Selection.ts | 12 +++ packages/cursorless-engine/package.json | 1 + .../processTargets/TargetPipelineRunner.ts | 9 ++- .../src/util/setSelectionsAndFocusEditor.ts | 8 +- .../cursorless-engine/src/util/uniqDeep.ts | 5 -- .../src/util/uniqWithHash.test.ts | 65 +++++++++++++++ .../src/util/uniqWithHash.ts | 81 +++++++++++++++++++ pnpm-lock.yaml | 10 +++ 10 files changed, 206 insertions(+), 9 deletions(-) delete mode 100644 packages/cursorless-engine/src/util/uniqDeep.ts create mode 100644 packages/cursorless-engine/src/util/uniqWithHash.test.ts create mode 100644 packages/cursorless-engine/src/util/uniqWithHash.ts diff --git a/packages/common/src/types/Position.ts b/packages/common/src/types/Position.ts index 79f4d9063b..e4b423218b 100644 --- a/packages/common/src/types/Position.ts +++ b/packages/common/src/types/Position.ts @@ -138,4 +138,16 @@ export class Position { public toEmptyRange(): Range { return new Range(this, this); } + + /** + * Return a concise string representation of the position. + * @returns concise representation + **/ + public concise(): string { + return `${this.line}:${this.character}`; + } + + public toString(): string { + return this.concise(); + } } diff --git a/packages/common/src/types/Range.ts b/packages/common/src/types/Range.ts index 55ea3b4029..b0ffe057f3 100644 --- a/packages/common/src/types/Range.ts +++ b/packages/common/src/types/Range.ts @@ -146,4 +146,16 @@ export class Range { ? new Selection(this.end, this.start) : new Selection(this.start, this.end); } + + /** + * Return a concise string representation of the range + * @returns concise representation + **/ + public concise(): string { + return `${this.start.concise()}-${this.end.concise()}`; + } + + public toString(): string { + return this.concise(); + } } diff --git a/packages/common/src/types/Selection.ts b/packages/common/src/types/Selection.ts index f54ac06787..a202c47251 100644 --- a/packages/common/src/types/Selection.ts +++ b/packages/common/src/types/Selection.ts @@ -72,4 +72,16 @@ export class Selection extends Range { this.anchor.isEqual(other.anchor) && this.active.isEqual(other.active) ); } + + /** + * Return a concise string representation of the selection + * @returns concise representation + **/ + public concise(): string { + return `${this.anchor.concise()}->${this.active.concise()}`; + } + + public toString(): string { + return this.concise(); + } } diff --git a/packages/cursorless-engine/package.json b/packages/cursorless-engine/package.json index 717d13ad20..27cd57db23 100644 --- a/packages/cursorless-engine/package.json +++ b/packages/cursorless-engine/package.json @@ -27,6 +27,7 @@ "@types/mocha": "^8.0.4", "@types/sbd": "^1.0.3", "@types/sinon": "^10.0.2", + "fast-check": "3.12.0", "js-yaml": "^4.1.0", "mocha": "^10.2.0", "sinon": "^11.1.1" diff --git a/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts b/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts index c5f147d8b0..407d6b93a0 100644 --- a/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts +++ b/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts @@ -5,7 +5,7 @@ import { Range, ScopeType, } from "@cursorless/common"; -import { uniqWith, zip } from "lodash"; +import { zip } from "lodash"; import { PrimitiveTargetDescriptor, RangeTargetDescriptor, @@ -18,6 +18,7 @@ import { MarkStage, ModifierStage } from "./PipelineStages.types"; import ImplicitStage from "./marks/ImplicitStage"; import { ContainingTokenIfUntypedEmptyStage } from "./modifiers/ConditionalModifierStages"; import { PlainTarget } from "./targets"; +import { uniqWithHash } from "../util/uniqWithHash"; export class TargetPipelineRunner { constructor( @@ -287,7 +288,11 @@ function calcIsReversed(anchor: Target, active: Target) { } function uniqTargets(array: Target[]): Target[] { - return uniqWith(array, (a, b) => a.isEqual(b)); + return uniqWithHash( + array, + (a, b) => a.isEqual(b), + (a) => a.contentRange.concise(), + ); } function ensureSingleEditor(anchorTarget: Target, activeTarget: Target) { diff --git a/packages/cursorless-engine/src/util/setSelectionsAndFocusEditor.ts b/packages/cursorless-engine/src/util/setSelectionsAndFocusEditor.ts index 2b8b318a22..404daefc54 100644 --- a/packages/cursorless-engine/src/util/setSelectionsAndFocusEditor.ts +++ b/packages/cursorless-engine/src/util/setSelectionsAndFocusEditor.ts @@ -1,6 +1,6 @@ import { EditableTextEditor, Selection } from "@cursorless/common"; -import uniqDeep from "./uniqDeep"; +import { uniqWithHash } from "./uniqWithHash"; export async function setSelectionsAndFocusEditor( editor: EditableTextEditor, @@ -22,5 +22,9 @@ export function setSelectionsWithoutFocusingEditor( editor: EditableTextEditor, selections: Selection[], ) { - editor.selections = uniqDeep(selections); + editor.selections = uniqWithHash( + selections, + (a, b) => a.isEqual(b), + (s) => s.concise(), + ); } diff --git a/packages/cursorless-engine/src/util/uniqDeep.ts b/packages/cursorless-engine/src/util/uniqDeep.ts deleted file mode 100644 index 182226a01e..0000000000 --- a/packages/cursorless-engine/src/util/uniqDeep.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { uniqWith, isEqual } from "lodash"; - -export default (array: T[]): T[] => { - return uniqWith(array, isEqual); -}; diff --git a/packages/cursorless-engine/src/util/uniqWithHash.test.ts b/packages/cursorless-engine/src/util/uniqWithHash.test.ts new file mode 100644 index 0000000000..5e6be110ed --- /dev/null +++ b/packages/cursorless-engine/src/util/uniqWithHash.test.ts @@ -0,0 +1,65 @@ +import * as assert from "assert"; +import * as fc from "fast-check"; +import { uniqWith } from "lodash"; +import { uniqWithHash } from "./uniqWithHash"; + +// known good but slow (quadratic!) +function knownGoodUniqWithHash( + array: T[], + fn: (a: T, b: T) => boolean, + _: (t: T) => string, +): T[] { + return uniqWith(array, fn); +} + +suite("uniqWithHash", () => { + test("uniqWithHash", () => { + // believe it or not, all these test cases are important + const testCases: number[][] = [ + [], + [0], + [1], + [0, 1], + [1, 0], + [0, 0], + [1, 1], + [0, 1, 0, 1], + [1, 0, 1, 0], + [0, 1, 1, 0], + [1, 0, 0, 1], + [0, 1, 2, 3], + [0, 1, 2, 3, 0, 1, 2, 3], + [0, 0, 1, 1, 2, 2, 3, 3], + [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3], + [0, 0, 1, 1, 2, 2, 3, 3, 0, 0, 1, 1, 2, 2, 3, 3], + [0, 1, 2, 3, 4, 5, 0, 1, 2, 3], + [0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 0, 1, 2, 3], + [0, 0, 1, 1, 2, 2, 3, 3, 4, 4], + [0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4], + ]; + + const hashFunctions = [ + (x: number) => x.toString(), + (x: number) => (x % 2).toString(), + (_: number) => "0", + ]; + + hashFunctions.forEach((hash) => { + const check = (testCase: number[]) => { + const actual = uniqWithHash(testCase, (a, b) => a === b, hash); + const expected = knownGoodUniqWithHash( + testCase, + (a, b) => a === b, + hash, + ); + assert.deepStrictEqual(actual, expected); + }; + + testCases.forEach(check); + + // max length 50 because the known good implementation is quadratic + const randomNumbers = fc.array(fc.integer(), { maxLength: 50 }); + fc.assert(fc.property(randomNumbers, check)); + }); + }); +}); diff --git a/packages/cursorless-engine/src/util/uniqWithHash.ts b/packages/cursorless-engine/src/util/uniqWithHash.ts new file mode 100644 index 0000000000..16221ce7b0 --- /dev/null +++ b/packages/cursorless-engine/src/util/uniqWithHash.ts @@ -0,0 +1,81 @@ +import { uniqWith } from "lodash"; + +/** + * Like lodash.uniqWith, but uses a hash function to (mostly) avoid quadratic runtime. + * @param array The array to uniq + * @param isEqual The equality function + * @param hash The hash function. It must be a valid hash function, insofar as it must return the same value for equal items. A hash function that returns a constant value is equivalent to lodash.uniqWith. + * @returns The uniq array + */ +export function uniqWithHash( + array: T[], + isEqual: (a: T, b: T) => boolean, + hash: (t: T) => string, +): T[] { + // Handle the common, tiny cases without allocating anything extra. + if (array.length < 2) { + return [...array]; + } + if (array.length === 2) { + if (isEqual(array[0], array[1])) { + return [array[0]]; + } + return [...array]; + } + // First, split up the array using the hash function. + // This keeps the sets of items passed to uniqWith small, + // so that the quadratic runtime of uniqWith less of a problem. + + /* Keep track of which sets have multiple items, so that we can uniq them. */ + const needsUniq: string[] = []; + const hashToItems: Map = array.reduce((acc, item) => { + const key = hash(item); + const items = acc.get(key); + if (items == null) { + acc.set(key, [item]); + return acc; + } + + acc.get(key)!.push(item); + if (items.length === 2) { + needsUniq.push(key); + } + return acc; + }, new Map()); + + // Another common case: Everything is unique. + if (needsUniq.length === 0) { + return [...array]; + } + + // For hash collisions, uniq the items, + // letting uniqWith provide correct semantics. + needsUniq.forEach((key) => { + hashToItems.set(key, uniqWith(hashToItems.get(key)!, isEqual)); + }); + + // To preserve order, step through the original items + // one at a time, returning it as appropriate. + // We need to do this because uniqWith preserves order, + // and we are mimicking its semantics. + // Note that items were added in order, + // and uniqWith preserved that order. + return array.flatMap((item) => { + const key = hash(item); + const items = hashToItems.get(key)!; + if (items == null || items.length === 0) { + // Removed by uniqWith. + return []; + } + const first = items[0]!; + if (!isEqual(first, item)) { + // Removed by uniqWith. + // Note that it is sufficient to check the first item, + // because uniqWith preserves order. + return []; + } + // Emit item. + items.shift(); + return first; + }); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d56301c47d..0aabb70b61 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -257,6 +257,9 @@ importers: '@types/sinon': specifier: ^10.0.2 version: 10.0.13 + fast-check: + specifier: 3.12.0 + version: 3.12.0 js-yaml: specifier: ^4.1.0 version: 4.1.0 @@ -8090,6 +8093,13 @@ packages: iconv-lite: 0.4.24 tmp: 0.0.33 + /fast-check@3.12.0: + resolution: {integrity: sha512-SqahE9mlL3+lhjJ39joMLwcj6F+24hfZdf/tchlNO8sHcTdrUUdA5P/ZbSFZM9Xpzs36XaneGwE0FWepm/zyOA==} + engines: {node: '>=8.0.0'} + dependencies: + pure-rand: 6.0.0 + dev: true + /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} From bf24769e3fe63dfec13e7c15b22e8c03b190a3b5 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Sun, 3 Sep 2023 10:39:09 +0100 Subject: [PATCH 08/54] Fix spoken form in 0.28.0.md (#1842) ## Checklist - [ ] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [ ] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [ ] I have not broken the cheatsheet --- docs/user/release-notes/0.28.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user/release-notes/0.28.0.md b/docs/user/release-notes/0.28.0.md index 47da0b8523..f832190ae5 100644 --- a/docs/user/release-notes/0.28.0.md +++ b/docs/user/release-notes/0.28.0.md @@ -27,7 +27,7 @@ But here are some highlights: - Added [the scope visualizer](../scope-visualizer.md) ([#1653](https://github.com/cursorless-dev/cursorless/pull/1653)) to let you visualize scopes live in your editor ([video 🎬](https://youtu.be/BbXEzUrf5lU)). And here's a screenshot ![Scope visualizer](../images/visualize-funk.png) - A next-gen inference engine that enables a much more powerful grammar, allowing you to do things like individually targeting a sequence of scopes, eg `"every line air past bat"`. To learn more, see the initial [#1462](https://github.com/cursorless-dev/cursorless/pull/1462), and then the follow-up [#1463](https://github.com/cursorless-dev/cursorless/pull/1463) if you're addicted to 🤯. Note that this grammar only works for our next-gen scopes, which include most generic scopes (`"line"`, `"token"`, `"word"`, `"character"`, `"block"`), as well as some language-specific scopes. We still have a lot of language-specific scopes to upgrade, as the infrastructure to do so is still quite new. Stay tuned - [Stable hats](../hatAssignment.md) ([#1252](https://github.com/cursorless-dev/cursorless/pull/1252)) keep hats from moving around so much while you edit. This change is something you may or may not have consciously noticed, but if you spontaneously experienced a deep feeling of oneness and calm at some point in the past six months, the newly meditative hats might have been the cause 🎩🧘 -- Improved support for very large hats. A trend among users has been to slightly increase their line height (eg 1.6) and then use a very large hat size (eg 70) to make the hats more visible. This works great, but adjacent hats would overlap. Now, we automatically detect clashing hats and stop them from growing horizontally if they'd overlap ([#1687](https://github.com/cursorless-dev/cursorless/pull/1687)). @pokey is now running with a line height of 1.6 and a hat size of 70, after getting serious hat-size envy during some user screenshares on discord 👒.![Big hats](big-hats.png) If you want to join the big hat revolution, say `"show settings phrase line height"` to mess with your line height, then `"cursorless settings"` to mess with your hat size +- Improved support for very large hats. A trend among users has been to slightly increase their line height (eg 1.6) and then use a very large hat size (eg 70) to make the hats more visible. This works great, but adjacent hats would overlap. Now, we automatically detect clashing hats and stop them from growing horizontally if they'd overlap ([#1687](https://github.com/cursorless-dev/cursorless/pull/1687)). @pokey is now running with a line height of 1.6 and a hat size of 70, after getting serious hat-size envy during some user screenshares on discord 👒.![Big hats](big-hats.png) If you want to join the big hat revolution, say `"show settings say line height"` to mess with your line height, then `"cursorless settings"` to mess with your hat size - Lots of improvements to our Go language support (thanks [@josharian](https://github.com/josharian)!) - Expanded our [Talon-side custom API](../customization.md#public-talon-actions) to - allow [snippet wrapping / insertion](../customization.md#snippet-actions) ([#1329](https://github.com/cursorless-dev/cursorless/pull/1329)), used eg for [Cursorless mathfly 🎬](https://youtu.be/v0j2_W97_s0), and From c5ef8fc559302c721dff8dbf25b3501a5f83fbdd Mon Sep 17 00:00:00 2001 From: River <22485304+r-tae@users.noreply.github.com> Date: Mon, 4 Sep 2023 04:30:17 +1000 Subject: [PATCH 09/54] Fix period in absolute path munging test names (#1843) I use a `~/code/github.com/account/repo` structure, this meant that the `p.split(".")[0]` (which was intended to remove the `.yml` extension) instead named all recorded tests `../../../../../../../github` ## Checklist - [/] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [/] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [/] I have not broken the cheatsheet --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- packages/common/src/testUtil/getFixturePaths.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/common/src/testUtil/getFixturePaths.ts b/packages/common/src/testUtil/getFixturePaths.ts index bfe8765da0..034d3ceeff 100644 --- a/packages/common/src/testUtil/getFixturePaths.ts +++ b/packages/common/src/testUtil/getFixturePaths.ts @@ -28,7 +28,7 @@ export function getRecordedTestPaths() { return walkFilesSync(directory) .filter((p) => p.endsWith(".yml") || p.endsWith(".yaml")) .map((p) => ({ - name: path.relative(relativeDir, p.split(".")[0]), + name: path.relative(relativeDir, p.substring(0, p.lastIndexOf("."))), path: p, })); } From 6e2b5ff8a43f61d0b4920cf584b9994501c818d5 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Tue, 5 Sep 2023 17:20:00 -0700 Subject: [PATCH 10/54] memoize minimumTokenRankContainingGrapheme (#1825) minimumTokenRankContainingGrapheme was accidentally quadratic in the number of tokens sharing a grapheme. It was executed for every token, and for each token, it considered every single other token sharing a candidate grapheme. It dominated hat allocation performance for larger numbers of tokens. Memoizing the result by grapheme text makes it linear again. No functional changes. (Confirmed by hat golden tests on another branch.) ## Checklist - [/] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [/] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [/] I have not broken the cheatsheet --------- Co-authored-by: Pokey Rule <755842+pokey@users.noreply.github.com> --- .../src/util/allocateHats/HatMetrics.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/cursorless-engine/src/util/allocateHats/HatMetrics.ts b/packages/cursorless-engine/src/util/allocateHats/HatMetrics.ts index 3aae820a47..cd747f57a4 100644 --- a/packages/cursorless-engine/src/util/allocateHats/HatMetrics.ts +++ b/packages/cursorless-engine/src/util/allocateHats/HatMetrics.ts @@ -1,5 +1,5 @@ import { CompositeKeyMap, HatStability, TokenHat } from "@cursorless/common"; -import { min } from "lodash"; +import { memoize, min } from "lodash"; import { HatCandidate } from "./allocateHats"; /** @@ -50,8 +50,13 @@ export function minimumTokenRankContainingGrapheme( tokenRank: number, graphemeTokenRanks: { [key: string]: number[] }, ): HatMetric { - return ({ grapheme: { text } }) => - min(graphemeTokenRanks[text].filter((r) => r > tokenRank)) ?? Infinity; + const coreMetric = memoize((graphemeText: string): number => { + return ( + min(graphemeTokenRanks[graphemeText].filter((r) => r > tokenRank)) ?? + Infinity + ); + }); + return ({ grapheme: { text } }) => coreMetric(text); } /** From 0b984fc02575339a31a28d74f7f87311c43c2d4d Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Tue, 5 Sep 2023 21:15:49 -0700 Subject: [PATCH 11/54] optimize getTokenRemainingHatCandidates (#1838) Object.entries + map allocates a copy of the list twice. And it ends up calling a tiny function over and over. Using a boring nested for loop and an array cuts hat allocation time by about 35%. Removing items from an array one at a time is quadratic (triangular). However, N is capped at the number of hats a user has (per grapheme), which is fundamentally capped at a fairly small number. And copying an array is typically a highly optimized operation. This bit of hot code showed up in a profile from user saidelike on Slack. I have tested that this does not modify behavior on a branch with golden hat tests. ## Checklist - [/] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [/] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [/] I have not broken the cheatsheet --------- Co-authored-by: Pokey Rule <755842+pokey@users.noreply.github.com> --- .../src/util/allocateHats/allocateHats.ts | 56 ++++++++++++------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/packages/cursorless-engine/src/util/allocateHats/allocateHats.ts b/packages/cursorless-engine/src/util/allocateHats/allocateHats.ts index 5c22ffab46..10c4380256 100644 --- a/packages/cursorless-engine/src/util/allocateHats/allocateHats.ts +++ b/packages/cursorless-engine/src/util/allocateHats/allocateHats.ts @@ -9,7 +9,6 @@ import { Token, TokenHat, } from "@cursorless/common"; -import { clone } from "lodash"; import { Grapheme, TokenGraphemeSplitter } from "../../tokenGraphemeSplitter"; import { chooseTokenHat } from "./chooseTokenHat"; import { getHatRankingContext } from "./getHatRankingContext"; @@ -69,13 +68,19 @@ export function allocateHats( tokenGraphemeSplitter, ); + /* All initially enabled hat styles. */ + const enabledHatStyleNames = Object.keys(enabledHatStyles); + /** * A map from graphemes to the remaining hat styles that have not yet been * used for that grapheme. As we assign hats to tokens, we remove them from - * these lists so that they don't get used again in this pass. + * these arrays so that they don't get used again in this pass. + * We use an array rather than a map to make iteration cheaper; + * we only remove elements once, but we iterate many times, + * and this is a hot path. */ - const graphemeRemainingHatCandidates = new DefaultMap( - () => clone(enabledHatStyles), + const graphemeRemainingHatCandidates = new DefaultMap( + () => [...enabledHatStyleNames], ); // Iterate through tokens in order of decreasing rank, assigning each one a @@ -90,6 +95,7 @@ export function allocateHats( tokenGraphemeSplitter, token, graphemeRemainingHatCandidates, + enabledHatStyles, ); const chosenHat = chooseTokenHat( @@ -107,10 +113,12 @@ export function allocateHats( } // Remove the hat we chose from consideration for lower ranked tokens - delete graphemeRemainingHatCandidates.get(chosenHat.grapheme.text)[ - chosenHat.style - ]; - + graphemeRemainingHatCandidates.set( + chosenHat.grapheme.text, + graphemeRemainingHatCandidates + .get(chosenHat.grapheme.text) + .filter((style) => style !== chosenHat.style), + ); return constructHatRangeDescriptor(token, chosenHat); }) .filter((value): value is TokenHat => value != null); @@ -135,19 +143,27 @@ function getTokenOldHatMap(oldTokenHats: readonly TokenHat[]) { function getTokenRemainingHatCandidates( tokenGraphemeSplitter: TokenGraphemeSplitter, token: Token, - availableGraphemeStyles: DefaultMap, + graphemeRemainingHatCandidates: DefaultMap, + enabledHatStyles: HatStyleMap, ): HatCandidate[] { - return tokenGraphemeSplitter - .getTokenGraphemes(token.text) - .flatMap((grapheme) => - Object.entries(availableGraphemeStyles.get(grapheme.text)).map( - ([style, { penalty }]) => ({ - grapheme, - style, - penalty, - }), - ), - ); + // Use iteration here instead of functional constructs, + // because this is a hot path and we want to avoid allocating arrays + // and calling tiny functions lots of times. + const candidates: HatCandidate[] = []; + const graphemes = tokenGraphemeSplitter.getTokenGraphemes(token.text); + for (const grapheme of graphemes) { + for (const style of graphemeRemainingHatCandidates.get(grapheme.text)) { + // Allocating and pushing all of these objects is + // the single most expensive thing in hat allocation. + // Please pay attention to performance when modifying this code. + candidates.push({ + grapheme, + style, + penalty: enabledHatStyles[style].penalty, + }); + } + } + return candidates; } /** From 1df6f0255e33401fcac8a82b03e082bd3910a93f Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Tue, 5 Sep 2023 21:17:11 -0700 Subject: [PATCH 12/54] fix typos (#1856) My OCD finds them mildly distracting. ## Checklist - [/] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [/] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [/] I have not broken the cheatsheet --- docs/user/experimental/keyboard/modal.md | 2 +- .../src/actions/GenerateSnippet/GenerateSnippet.ts | 2 +- packages/cursorless-engine/src/core/HatTokenMapImpl.ts | 2 +- .../cursorless-engine/src/core/compareSnippetDefinitions.ts | 2 +- .../src/core/updateSelections/updateSelections.ts | 2 +- packages/cursorless-engine/src/languages/branchMatcher.ts | 2 +- .../src/processTargets/modifiers/EveryScopeStage.ts | 2 +- packages/cursorless-engine/src/tokenizer/tokenizer.ts | 2 +- .../cursorless-engine/src/util/allocateHats/allocateHats.ts | 2 +- packages/cursorless-engine/src/util/unifyRanges.ts | 2 +- .../cursorless-vscode/src/keyboard/KeyboardCommandsModal.ts | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/user/experimental/keyboard/modal.md b/docs/user/experimental/keyboard/modal.md index 375dd0bff8..d46d2c9d36 100644 --- a/docs/user/experimental/keyboard/modal.md +++ b/docs/user/experimental/keyboard/modal.md @@ -99,7 +99,7 @@ To bind keys that do not have modifiers (eg just pressing `a`), add entries like }, ``` -Any supported scopes, actions, or colors can be added to these sections, using the same identifierss that appear in the second column of your customisation csvs. Feel free to add / tweak / remove the keyboard shortcuts above as you see fit. +Any supported scopes, actions, or colors can be added to these sections, using the same identifiers that appear in the second column of your customisation csvs. Feel free to add / tweak / remove the keyboard shortcuts above as you see fit. The above allows you to press `d` followed by any letter to highlight the given token, `i` to expand to its containing line, and `t` to select the given target. diff --git a/packages/cursorless-engine/src/actions/GenerateSnippet/GenerateSnippet.ts b/packages/cursorless-engine/src/actions/GenerateSnippet/GenerateSnippet.ts index b9e78aa141..e41a35de4b 100644 --- a/packages/cursorless-engine/src/actions/GenerateSnippet/GenerateSnippet.ts +++ b/packages/cursorless-engine/src/actions/GenerateSnippet/GenerateSnippet.ts @@ -175,7 +175,7 @@ export default class GenerateSnippet { // as wrapperScopeType. Ie the output will look like `{|}` (with the `|` // representing a tabstop in the meta-snippet) // - // NB: We use the subsituter here, with `isQuoted=true` because in order + // NB: We use the substituter here, with `isQuoted=true` because in order // to make this work for the meta-snippet, we want to end up with // something like `{$3}`, which is not valid json. So we instead arrange // to end up with json like `"hgidfsivhs"`, and then replace the whole diff --git a/packages/cursorless-engine/src/core/HatTokenMapImpl.ts b/packages/cursorless-engine/src/core/HatTokenMapImpl.ts index 559375d93b..2df45a8859 100644 --- a/packages/cursorless-engine/src/core/HatTokenMapImpl.ts +++ b/packages/cursorless-engine/src/core/HatTokenMapImpl.ts @@ -23,7 +23,7 @@ const PRE_PHRASE_SNAPSHOT_MAX_AGE_NS = BigInt(6e10); // 60 seconds */ export class HatTokenMapImpl implements HatTokenMap { /** - * This is the active map the changes every time we reallocate hats. It is + * This is the active map that changes every time we reallocate hats. It is * liable to change in the middle of a phrase. */ private activeMap: IndividualHatMap; diff --git a/packages/cursorless-engine/src/core/compareSnippetDefinitions.ts b/packages/cursorless-engine/src/core/compareSnippetDefinitions.ts index 794282db2f..cff4816ece 100644 --- a/packages/cursorless-engine/src/core/compareSnippetDefinitions.ts +++ b/packages/cursorless-engine/src/core/compareSnippetDefinitions.ts @@ -21,7 +21,7 @@ export function compareSnippetDefinitions( b.definition.scope, ); - // Prefer the more specific snippet definitino, no matter the origin + // Prefer the more specific snippet definition, no matter the origin if (scopeComparision !== 0) { return scopeComparision; } diff --git a/packages/cursorless-engine/src/core/updateSelections/updateSelections.ts b/packages/cursorless-engine/src/core/updateSelections/updateSelections.ts index fe7e1bd9b8..c7f8c5246c 100644 --- a/packages/cursorless-engine/src/core/updateSelections/updateSelections.ts +++ b/packages/cursorless-engine/src/core/updateSelections/updateSelections.ts @@ -363,7 +363,7 @@ export async function performEditsAndUpdateFullSelectionInfos( originalSelectionInfos: FullSelectionInfo[][], ): Promise { // NB: We do everything using VSCode listeners. We can associate changes - // with our changes just by looking at their offets / text in order to + // with our changes just by looking at their offsets / text in order to // recover isReplace. We need to do this because VSCode does some fancy // stuff, and returns the changes in a nice order // Note that some additional weird edits like whitespace things can be diff --git a/packages/cursorless-engine/src/languages/branchMatcher.ts b/packages/cursorless-engine/src/languages/branchMatcher.ts index 7293bdd577..63b93d874f 100644 --- a/packages/cursorless-engine/src/languages/branchMatcher.ts +++ b/packages/cursorless-engine/src/languages/branchMatcher.ts @@ -15,7 +15,7 @@ import { childRangeSelector } from "../util/nodeSelectors"; * "if_statement" or "try_statement" * @param optionalBranchTypes The optional branch type names that can be * children of the top-level statement, eg "else_clause" or "except_clause" - * @returns A node matcher capabale of matching this type of branch + * @returns A node matcher capable of matching this type of branch */ export function branchMatcher( statementType: string, diff --git a/packages/cursorless-engine/src/processTargets/modifiers/EveryScopeStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/EveryScopeStage.ts index cb4c77d9f8..74785bb372 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/EveryScopeStage.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/EveryScopeStage.ts @@ -65,7 +65,7 @@ export class EveryScopeStage implements ModifierStage { ) { // If the only scope that came back completely contains the input target // range, we treat the input as if it had no explicit range, expanding - // to default iteration socpe below + // to default iteration scope below scopes = undefined; } } diff --git a/packages/cursorless-engine/src/tokenizer/tokenizer.ts b/packages/cursorless-engine/src/tokenizer/tokenizer.ts index 2f9b25287a..75b7145cb7 100644 --- a/packages/cursorless-engine/src/tokenizer/tokenizer.ts +++ b/packages/cursorless-engine/src/tokenizer/tokenizer.ts @@ -43,7 +43,7 @@ const FIXED_TOKENS = [ export const IDENTIFIER_WORD_REGEXES = ["\\p{L}", "\\p{M}", "\\p{N}"]; const SINGLE_SYMBOLS_REGEX = "[^\\s\\w]"; // Accepts digits dot digits if not preceded or followed by a digit or dot. The -// negative lookahed / lookbehind are to prevent matching numbers in semantic +// negative lookahead / lookbehind are to prevent matching numbers in semantic // versions (eg 1.2.3) const NUMBERS_REGEX = "(? | undefined = this.mergedKeymap[sequence]; // We handle multi-key sequences by repeatedly awaiting a single keypress - // until they've pressed somethign in the map. + // until they've pressed something in the map. while (keyHandler == null) { if (!this.isPrefixOfKey(sequence)) { const errorMessage = `Unknown key sequence "${sequence}"`; From 60f53cee8f1dac64a5bee859d885d8fb9a59bfbf Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Fri, 8 Sep 2023 11:05:57 +0100 Subject: [PATCH 13/54] Disable talonfmt (#1872) As a workaround for #1865, we're disabling talonfmt for now. This workaround should be fine for a few days, as we rarely touch our talon files ## Checklist - [ ] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [ ] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [ ] I have not broken the cheatsheet --- .pre-commit-config.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ed1c7e0628..65150c9f46 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -83,8 +83,3 @@ repos: rev: 23.3.0 hooks: - id: black - - repo: https://github.com/wenkokke/talonfmt - rev: 1.9.5 - hooks: - - id: talonfmt - args: ["--in-place"] From d00e7c06f648cf5aee691a589deed8d4742bfd4d Mon Sep 17 00:00:00 2001 From: David Vo Date: Fri, 8 Sep 2023 20:34:40 +1000 Subject: [PATCH 14/54] Stop checking out repo in CodeQL merge queue hack (#1827) ## Checklist - [ ] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [ ] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [ ] I have not broken the cheatsheet --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- .github/workflows/codeql.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index df1b46c289..a1c0e1c20f 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -96,5 +96,5 @@ jobs: # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support steps: - - name: Checkout repository - uses: actions/checkout@v3 + - name: Do nothing + run: "true" From 376f9f57ac5e461e8cfb641dedd3fd46f11854e9 Mon Sep 17 00:00:00 2001 From: Cedric Halbronn <387346+saidelike@users.noreply.github.com> Date: Fri, 8 Sep 2023 12:45:30 +0100 Subject: [PATCH 15/54] Migrate python scopes map/list (#1874) ## Checklist - [/] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [/] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [/] I have not broken the cheatsheet - [x] I have executed all existing tests (Extension tests - 6277 passing) - [x] I have tested the `visualize list` and `visualize map` commands visualize list: ![image](https://github.com/cursorless-dev/cursorless/assets/387346/9e160f0d-2af7-4d75-a611-7ae54518fbaf) visualize map: ![image](https://github.com/cursorless-dev/cursorless/assets/387346/cf4dc273-2ec7-4be8-a0ab-e291ff6e9450) --------- Co-authored-by: Cedric Halbronn Co-authored-by: Pokey Rule <755842+pokey@users.noreply.github.com> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: Andreas Arvidsson --- data/playground/python/maps_and_lists.py | 34 +++++++++++++++++++ .../cursorless-engine/src/languages/python.ts | 5 --- queries/python.scm | 11 ++++++ 3 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 data/playground/python/maps_and_lists.py diff --git a/data/playground/python/maps_and_lists.py b/data/playground/python/maps_and_lists.py new file mode 100644 index 0000000000..3e1ccdd83c --- /dev/null +++ b/data/playground/python/maps_and_lists.py @@ -0,0 +1,34 @@ +import sys + +one = 1 + + +def mapsAndLists(): + # immutable can serve as dictionary keys + key1 = "a" + key2 = False + key3 = 1.5 + + # Python "tuple" is "pair"/"round" scope + _ = (1, 2, 3) + + # Python "list" is "list" scope + _ = [1, 2, 3] + _ = [sys.path[0], sys.path[1]] + fruits = ["apple", "banana", "cherry", "kiwi", "mango"] + _ = [x for x in fruits if "a" in x] + + # Python "set" is "list" scope + _ = {1, 2, 3} + + # Python "dictionary" is "map" scope + d1 = {"a": 1, "b": 2, "c": 3} + _ = {key1: 1, key2: 2, key3: 3} + _ = {value: key for (key, value) in d1.items()} + + # complex ones + _ = [[1, 2, 3], 4, 5, 6] + _ = [[1, 2, 3], {1, 2, 3}, {"a": 1, "b": 2, "c": 3}] + _ = {{"a": 1, "b": 2, "c": 3}: 1, d1: 2} + _ = {{1, 2, 3}: 1, {2, 3, 4}: 2} + _ = ({1, 2, 3}, {1, 2, 3}, {1, 2, 3}) diff --git a/packages/cursorless-engine/src/languages/python.ts b/packages/cursorless-engine/src/languages/python.ts index b29611b705..d4d59d335f 100644 --- a/packages/cursorless-engine/src/languages/python.ts +++ b/packages/cursorless-engine/src/languages/python.ts @@ -23,9 +23,6 @@ import { ternaryBranchMatcher } from "./ternaryBranchMatcher"; export const getTypeNode = (node: SyntaxNode) => node.children.find((child) => child.type === "type") ?? null; -const dictionaryTypes = ["dictionary", "dictionary_comprehension"]; -const listTypes = ["list", "list_comprehension", "set"]; - function itemNodeFinder( parentType: string, childType: string, @@ -48,8 +45,6 @@ function itemNodeFinder( const nodeMatchers: Partial< Record > = { - map: dictionaryTypes, - list: listTypes, string: "string", collectionItem: cascadingMatcher( matcher( diff --git a/queries/python.scm b/queries/python.scm index 08c4b98bb9..8cb243b4cf 100644 --- a/queries/python.scm +++ b/queries/python.scm @@ -27,6 +27,17 @@ (with_statement) ] @statement +[ + (dictionary) + (dictionary_comprehension) +] @map + +[ + (list) + (list_comprehension) + (set) +] @list + ( (function_definition name: (_) @functionName From 77502849ce85e025fc4fde855ace6be593ffb773 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Fri, 8 Sep 2023 22:19:24 +0100 Subject: [PATCH 16/54] Fix text fragment extractor fallbacks (#1863) The fallback logic for determining whether to use text-based surrounding pairs or parse-tree-based was broken for text fragment extractors based on next-gen scope handlers (ie using the new `@textFragment` tag in a query file). This breakage meant that in a string like `"""hello"""` in Python, the surrounding pair finder fell back to text-based, which resulted in it thinking `"hello"` was a string, rather than `"""hello"""`, as would happen if it fell back to parse-tree-based This PR fixes the issue by unifying the fallback logic between legacy and next-gen text fragment extractors, so that they don't fall out of sync again See also https://github.com/cursorless-dev/cursorless/issues/1812#issuecomment-1691493746; arguably that will make this PR irrelevant, but until then, it's better to have string not be broken when we migrate a language to use `@textFragment` Note that the tests in this PR don't actually test the code yet, because Python is still using legacy text fragment extractors. The tests will start biting when this PR is merged into main and then merged into https://github.com/cursorless-dev/cursorless/pull/1862 - This PR is required by https://github.com/cursorless-dev/cursorless/pull/1862 ## Checklist - [ ] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [ ] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [ ] I have not broken the cheatsheet --------- Co-authored-by: Andreas Arvidsson --- .../src/languages/getTextFragmentExtractor.ts | 12 +-- .../modifiers/surroundingPair/index.ts | 81 ++++++++----------- .../parseTree/python/changeString.yml | 23 ++++++ .../parseTree/python/changeString3.yml | 23 ++++++ .../parseTree/python/changeString4.yml | 23 ++++++ .../parseTree/python/changeString5.yml | 23 ++++++ .../parseTree/python/changeString6.yml | 23 ++++++ .../parseTree/python/changeString7.yml | 23 ++++++ .../parseTree/python/changeString8.yml | 23 ++++++ 9 files changed, 199 insertions(+), 55 deletions(-) create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString3.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString4.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString5.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString6.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString7.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString8.yml diff --git a/packages/cursorless-engine/src/languages/getTextFragmentExtractor.ts b/packages/cursorless-engine/src/languages/getTextFragmentExtractor.ts index 3ec3d17be7..3e5ed7440a 100644 --- a/packages/cursorless-engine/src/languages/getTextFragmentExtractor.ts +++ b/packages/cursorless-engine/src/languages/getTextFragmentExtractor.ts @@ -1,4 +1,4 @@ -import { Range, UnsupportedLanguageError } from "@cursorless/common"; +import { Range } from "@cursorless/common"; import type { SyntaxNode } from "web-tree-sitter"; import { SelectionWithEditor } from "../typings/Types"; import { notSupported } from "../util/nodeMatchers"; @@ -104,14 +104,8 @@ function constructHackedStringTextFragmentExtractor( */ export default function getTextFragmentExtractor( languageId: string, -): TextFragmentExtractor { - const extractor = textFragmentExtractors[languageId as LegacyLanguageId]; - - if (extractor == null) { - throw new UnsupportedLanguageError(languageId); - } - - return extractor; +): TextFragmentExtractor | null { + return textFragmentExtractors[languageId as LegacyLanguageId]; } // NB: For now when we want use the entire file as a text fragment we just diff --git a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/index.ts b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/index.ts index 9b9337fbb3..52898d6e88 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/index.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/index.ts @@ -5,9 +5,7 @@ import { } from "@cursorless/common"; import type { SyntaxNode } from "web-tree-sitter"; import { LanguageDefinitions } from "../../../languages/LanguageDefinitions"; -import getTextFragmentExtractor, { - TextFragmentExtractor, -} from "../../../languages/getTextFragmentExtractor"; +import getTextFragmentExtractor from "../../../languages/getTextFragmentExtractor"; import { Target } from "../../../typings/target.types"; import { SurroundingPairTarget } from "../../targets"; import { getContainingScopeTarget } from "../getContainingScopeTarget"; @@ -73,40 +71,6 @@ function processSurroundingPairCore( ] ?? [scopeType.delimiter]; let node: SyntaxNode | null; - let textFragmentExtractor: TextFragmentExtractor; - - const textFragmentScopeHandler = - languageDefinition?.getTextFragmentScopeHandler(); - - if (textFragmentScopeHandler != null) { - const containingScope = getContainingScopeTarget( - target, - textFragmentScopeHandler, - 0, - ); - - if (containingScope != null) { - const surroundingRange = findSurroundingPairTextBased( - editor, - range, - containingScope[0].contentRange, - delimiters, - scopeType, - ); - if (surroundingRange != null) { - // Found the pair within this text fragment or comment, e.g. "(abc)" - return surroundingRange; - } - // Search in the rest of the file, to find e.g. ("abc") - return findSurroundingPairTextBased( - editor, - range, - null, - delimiters, - scopeType, - ); - } - } try { node = languageDefinitions.getNodeAtLocation(document, range); @@ -122,8 +86,6 @@ function processSurroundingPairCore( scopeType, ); } - - textFragmentExtractor = getTextFragmentExtractor(document.languageId); } catch (err) { if ((err as Error).name === "UnsupportedLanguageError") { // If we're in a language where we don't have a parse tree we use the text @@ -140,14 +102,41 @@ function processSurroundingPairCore( } } - // If we have a parse tree but we are in a string node or in a comment node, - // then we use the text-based algorithm - const selectionWithEditor = { - editor, - selection: new Selection(range.start, range.end), - }; - const textFragmentRange = textFragmentExtractor(node, selectionWithEditor); + const textFragmentRange = (() => { + // First try to use the text fragment scope handler if it exists + const textFragmentScopeHandler = + languageDefinition?.getTextFragmentScopeHandler(); + + if (textFragmentScopeHandler != null) { + const containingScope = getContainingScopeTarget( + target, + textFragmentScopeHandler, + 0, + ); + + return containingScope?.[0].contentRange; + } + + // Then try to use the legacy text fragment extractor if it exists + const textFragmentExtractor = getTextFragmentExtractor(document.languageId); + + if (textFragmentExtractor == null) { + // If the text fragment extractor doesn't exist, or if it explicitly is + // set to `null`, then we just use text-based algorithm on entire document + return document.range; + } + + const selectionWithEditor = { + editor, + selection: new Selection(range.start, range.end), + }; + + return textFragmentExtractor(node, selectionWithEditor); + })(); + if (textFragmentRange != null) { + // If we have a parse tree but we are in a string node or in a comment node, + // then we use the text-based algorithm const surroundingRange = findSurroundingPairTextBased( editor, range, diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString.yml new file mode 100644 index 0000000000..0d5e90c5a8 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString.yml @@ -0,0 +1,23 @@ +languageId: python +command: + version: 6 + spokenForm: change string + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: surroundingPair, delimiter: string} + usePrePhraseSnapshot: true +initialState: + documentContents: "\"\"\"hello\"\"\"" + selections: + - anchor: {line: 0, character: 3} + active: {line: 0, character: 3} + marks: {} +finalState: + documentContents: "" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString3.yml new file mode 100644 index 0000000000..174464de8f --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString3.yml @@ -0,0 +1,23 @@ +languageId: python +command: + version: 6 + spokenForm: change string + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: surroundingPair, delimiter: string} + usePrePhraseSnapshot: true +initialState: + documentContents: "\"\"\"aaa\"\"\"" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: "" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString4.yml new file mode 100644 index 0000000000..8a9fa441fb --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString4.yml @@ -0,0 +1,23 @@ +languageId: python +command: + version: 6 + spokenForm: change string + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: surroundingPair, delimiter: string} + usePrePhraseSnapshot: false +initialState: + documentContents: "\"\"\"aaa\"\"\"" + selections: + - anchor: {line: 0, character: 9} + active: {line: 0, character: 9} + marks: {} +finalState: + documentContents: "" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString5.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString5.yml new file mode 100644 index 0000000000..f39b27b5dd --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString5.yml @@ -0,0 +1,23 @@ +languageId: python +command: + version: 6 + spokenForm: change string + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: surroundingPair, delimiter: string} + usePrePhraseSnapshot: false +initialState: + documentContents: f"""aaa""" + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} + marks: {} +finalState: + documentContents: "" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString6.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString6.yml new file mode 100644 index 0000000000..413c7e322c --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString6.yml @@ -0,0 +1,23 @@ +languageId: python +command: + version: 6 + spokenForm: change string + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: surroundingPair, delimiter: string} + usePrePhraseSnapshot: false +initialState: + documentContents: "'''aaa'''" + selections: + - anchor: {line: 0, character: 3} + active: {line: 0, character: 3} + marks: {} +finalState: + documentContents: "" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString7.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString7.yml new file mode 100644 index 0000000000..71d0220713 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString7.yml @@ -0,0 +1,23 @@ +languageId: python +command: + version: 6 + spokenForm: change string + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: surroundingPair, delimiter: string} + usePrePhraseSnapshot: false +initialState: + documentContents: "'aaa'" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: "" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString8.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString8.yml new file mode 100644 index 0000000000..ccadce753b --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/changeString8.yml @@ -0,0 +1,23 @@ +languageId: python +command: + version: 6 + spokenForm: change string + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: surroundingPair, delimiter: string} + usePrePhraseSnapshot: false +initialState: + documentContents: "'aaa'" + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} + marks: {} +finalState: + documentContents: "" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} From 162bcf7cb8623f9b92ef09e6da3d71f736783ecd Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sat, 9 Sep 2023 11:03:09 +0200 Subject: [PATCH 17/54] Made interface names targetable as keys (#1873) Fixes #1790 ## Checklist - [x] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [/] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [/] I have not broken the cheatsheet --- .../src/languages/typescript.ts | 1 + .../languages/typescript/changeKey.yml | 29 ++++++++++++++++ .../languages/typescript/changeKey2.yml | 29 ++++++++++++++++ .../languages/typescript/changeKey3.yml | 33 +++++++++++++++++++ 4 files changed, 92 insertions(+) create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeKey.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeKey2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeKey3.yml diff --git a/packages/cursorless-engine/src/languages/typescript.ts b/packages/cursorless-engine/src/languages/typescript.ts index 377f81d3ea..0ffd960729 100644 --- a/packages/cursorless-engine/src/languages/typescript.ts +++ b/packages/cursorless-engine/src/languages/typescript.ts @@ -166,6 +166,7 @@ const nodeMatchers: Partial< [ "pair[key]", "jsx_attribute.property_identifier!", + "object_type.property_signature[name]!", "shorthand_property_identifier", ], [":"], diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeKey.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeKey.yml new file mode 100644 index 0000000000..cd395f2c49 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeKey.yml @@ -0,0 +1,29 @@ +languageId: typescript +command: + version: 6 + spokenForm: change key + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: collectionKey} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + interface Hello { + value: number; + } + selections: + - anchor: {line: 1, character: 17} + active: {line: 1, character: 17} + marks: {} +finalState: + documentContents: |- + interface Hello { + : number; + } + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeKey2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeKey2.yml new file mode 100644 index 0000000000..fd692b5be8 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeKey2.yml @@ -0,0 +1,29 @@ +languageId: typescript +command: + version: 6 + spokenForm: change key + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: collectionKey} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + type Hello = { + value: number; + } + selections: + - anchor: {line: 1, character: 17} + active: {line: 1, character: 17} + marks: {} +finalState: + documentContents: |- + type Hello = { + : number; + } + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeKey3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeKey3.yml new file mode 100644 index 0000000000..18ac6d37e8 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeKey3.yml @@ -0,0 +1,33 @@ +languageId: typescript +command: + version: 6 + spokenForm: change key + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: collectionKey} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + function funk(): { + value: number; + } { + return { value: 2 } + } + selections: + - anchor: {line: 1, character: 17} + active: {line: 1, character: 17} + marks: {} +finalState: + documentContents: |- + function funk(): { + : number; + } { + return { value: 2 } + } + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} From 0b50c4a309dad6f2d5d76ea06dec597ca4b8d401 Mon Sep 17 00:00:00 2001 From: Cedric Halbronn <387346+saidelike@users.noreply.github.com> Date: Sat, 9 Sep 2023 10:06:47 +0100 Subject: [PATCH 18/54] Migrate Python to use next-gen scope handlers for text fragment extractors (#1862) - Depends on #1863 ## Checklist - [x] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [/] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [/] I have not broken the cheatsheet - [x] merge https://github.com/cursorless-dev/cursorless/pull/1858 first as seems required - [x] @pokey to fix a bug in the "processSurroundingPairCore" function atm that makes it not working atm (see commit message). It will be handled in a different commit before we can take this PR into account - [x] record a test for the fix by @pokey by saying "cursorless record" and using "change string" This PR also includes a few additional unit tests for Python strings. --------- Co-authored-by: Cedric Halbronn Co-authored-by: Pokey Rule <755842+pokey@users.noreply.github.com> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- data/playground/python/statements.py | 36 +++++++++++++++++++ data/playground/python/strings.py | 8 +++++ .../src/languages/getTextFragmentExtractor.ts | 1 - .../cursorless-engine/src/languages/python.ts | 2 -- .../recorded/languages/python/takeString2.yml | 24 +++++++++++++ .../recorded/languages/python/takeString3.yml | 24 +++++++++++++ .../recorded/languages/python/takeString4.yml | 26 ++++++++++++++ queries/python.scm | 7 ++++ 8 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 data/playground/python/statements.py create mode 100644 data/playground/python/strings.py create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/takeString2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/takeString3.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/takeString4.yml diff --git a/data/playground/python/statements.py b/data/playground/python/statements.py new file mode 100644 index 0000000000..d98c0b04af --- /dev/null +++ b/data/playground/python/statements.py @@ -0,0 +1,36 @@ +import sys + + +def statements(): + print(sys.path[0]) # comment 1 + print("hello") # comment 2 + + # the below statement has additional spaces after it + val = 1 == 2 + if val is True: + return + + # also the below has non-empty indentation + + c = range(10) + c.append(100) + x = 1 + x /= 2 + for i in range(10): + if i == 0: + continue + print(i) + break + + age = 120 + if age > 90: + print("You are too old to party, granny.") + elif age < 0: + print("You're yet to be born") + elif age >= 18: + print("You are allowed to party") + else: + print("You're too young to party") + + +statements() diff --git a/data/playground/python/strings.py b/data/playground/python/strings.py new file mode 100644 index 0000000000..98e07fd2b7 --- /dev/null +++ b/data/playground/python/strings.py @@ -0,0 +1,8 @@ +value = 3 + +a = "single quote string" +b = "double quote string" +c = """triple single quote string""" +d = """triple double quote string""" +e = r"literal string" +f = f"format string {value}" diff --git a/packages/cursorless-engine/src/languages/getTextFragmentExtractor.ts b/packages/cursorless-engine/src/languages/getTextFragmentExtractor.ts index 3e5ed7440a..07d9035a1b 100644 --- a/packages/cursorless-engine/src/languages/getTextFragmentExtractor.ts +++ b/packages/cursorless-engine/src/languages/getTextFragmentExtractor.ts @@ -159,7 +159,6 @@ const textFragmentExtractors: Record< "php", phpStringTextFragmentExtractor, ), - python: constructDefaultTextFragmentExtractor("python"), ruby: constructDefaultTextFragmentExtractor( "ruby", rubyStringTextFragmentExtractor, diff --git a/packages/cursorless-engine/src/languages/python.ts b/packages/cursorless-engine/src/languages/python.ts index d4d59d335f..d20bea22f5 100644 --- a/packages/cursorless-engine/src/languages/python.ts +++ b/packages/cursorless-engine/src/languages/python.ts @@ -45,7 +45,6 @@ function itemNodeFinder( const nodeMatchers: Partial< Record > = { - string: "string", collectionItem: cascadingMatcher( matcher( itemNodeFinder("import_from_statement", "dotted_name", true), @@ -61,7 +60,6 @@ const nodeMatchers: Partial< anonymousFunction: "lambda?.lambda", functionCall: "call", functionCallee: "call[function]", - comment: "comment", condition: cascadingMatcher( conditionMatcher("*[condition]"), diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/takeString2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/takeString2.yml new file mode 100644 index 0000000000..5255d44a5d --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/takeString2.yml @@ -0,0 +1,24 @@ +languageId: python +command: + version: 1 + spokenForm: take string + action: setSelection + targets: + - type: primitive + modifier: {type: containingScope, scopeType: string} +spokenFormError: Scope type 'string' +initialState: + documentContents: | + + value = """hello world""" + selections: + - anchor: {line: 1, character: 17} + active: {line: 1, character: 17} + marks: {} +finalState: + documentContents: | + + value = """hello world""" + selections: + - anchor: {line: 1, character: 8} + active: {line: 1, character: 25} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/takeString3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/takeString3.yml new file mode 100644 index 0000000000..2e95571eea --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/takeString3.yml @@ -0,0 +1,24 @@ +languageId: python +command: + version: 1 + spokenForm: take string + action: setSelection + targets: + - type: primitive + modifier: {type: containingScope, scopeType: string} +spokenFormError: Scope type 'string' +initialState: + documentContents: | + + value = r"hello world" + selections: + - anchor: {line: 1, character: 16} + active: {line: 1, character: 16} + marks: {} +finalState: + documentContents: | + + value = r"hello world" + selections: + - anchor: {line: 1, character: 8} + active: {line: 1, character: 22} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/takeString4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/takeString4.yml new file mode 100644 index 0000000000..d562f3523a --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/takeString4.yml @@ -0,0 +1,26 @@ +languageId: python +command: + version: 1 + spokenForm: take string + action: setSelection + targets: + - type: primitive + modifier: {type: containingScope, scopeType: string} +spokenFormError: Scope type 'string' +initialState: + documentContents: | + + w = "world" + value = f"hello {w}" + selections: + - anchor: {line: 2, character: 16} + active: {line: 2, character: 16} + marks: {} +finalState: + documentContents: | + + w = "world" + value = f"hello {w}" + selections: + - anchor: {line: 2, character: 8} + active: {line: 2, character: 20} diff --git a/queries/python.scm b/queries/python.scm index 8cb243b4cf..7566194e9a 100644 --- a/queries/python.scm +++ b/queries/python.scm @@ -27,6 +27,13 @@ (with_statement) ] @statement +(comment) @comment @textFragment + +(string + _ @textFragment.start.endOf + _ @textFragment.end.startOf +) @string + [ (dictionary) (dictionary_comprehension) From 5789094879106b0417f73b21d1c872d3cc6d618a Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Sun, 10 Sep 2023 10:13:40 +0100 Subject: [PATCH 19/54] Update docs on iteration range (#1881) ## Checklist - [ ] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [ ] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [ ] I have not broken the cheatsheet --- docs/user/glossary.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user/glossary.md b/docs/user/glossary.md index d2ec049fe6..ca7a2d12e7 100644 --- a/docs/user/glossary.md +++ b/docs/user/glossary.md @@ -16,7 +16,7 @@ The [range](#range) within which a [scope](#scope) is valid/active. For example, ## Iteration scope -A canonical [range](#range) for a given [scope type](#scope-type) that defines a list of sibling [scopes](#scope). For example, the iteration scope for `"arg"` is an argument list. The iteration scope is used for things like `"every"`, to select all sibling scopes. This allows us to say things like `"take every arg"`, `"pre every line"`, etc. +The canonical [scope](#scope-type) for a given [scope type](#scope-type) that defines the iteration range when `"every "` or eg `"first "` are issued without an explicit input range. For example, the iteration scope for `"arg"` is an argument list. Thus, when you say `"take every arg"`, you'll get every argument in the argument list containing your cursor, and `"take every arg air"` will select every argument in the argument list containing the `"air"` token. Note that the iteration scope can be ignored by giving an explicit range, eg `"take every arg air file"`, `"take every arg air past bat"`, etc. ## Insertion delimiter From 065f98aacb64ac75c76900fe4d78d50074ae3278 Mon Sep 17 00:00:00 2001 From: Cedric Halbronn <387346+saidelike@users.noreply.github.com> Date: Sun, 10 Sep 2023 15:55:14 +0100 Subject: [PATCH 20/54] Migrate python scope "value" to new scope handler (#1877) ## Checklist - [ ] I have executed the existing tests but they seem to fail for whatever reason. Not sure why (e.g. `takeValue.yml` from a quick glance - [x] I have tested the `visualize value` command and all the `take value` from this file and they all work fine ![image](https://github.com/cursorless-dev/cursorless/assets/387346/22206d81-a41b-4b2d-b949-fa8760692be0) If anyone has ideas why the unit tests don't work. NOTES: - I haven't included the assignment symbols like ".", "=", "/=" as I don't think they are needed when using the tree-sitter but let me know if you think otherwise. - the "return" in the "return_statement" is something that was ignored too as not relevant anymore afaict --------- Co-authored-by: Cedric Halbronn Co-authored-by: Pokey Rule <755842+pokey@users.noreply.github.com> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- data/playground/python/values.py | 64 ++++++++++++++++ .../cursorless-engine/src/languages/python.ts | 22 ------ .../recorded/languages/python/changeValue.yml | 27 +++++++ .../languages/python/changeValue2.yml | 29 +++++++ .../recorded/languages/python/chuckValue4.yml | 27 +++++++ .../recorded/languages/python/chuckValue5.yml | 29 +++++++ pyproject.toml | 3 +- queries/python.scm | 76 ++++++++++++++++++- 8 files changed, 252 insertions(+), 25 deletions(-) create mode 100644 data/playground/python/values.py create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeValue.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeValue2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/chuckValue4.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/chuckValue5.yml diff --git a/data/playground/python/values.py b/data/playground/python/values.py new file mode 100644 index 0000000000..e20b579375 --- /dev/null +++ b/data/playground/python/values.py @@ -0,0 +1,64 @@ +# Useful commands: +# "visualize value" +# "visualize value iteration" +# "take value" +# "chuck value" +# "chuck every value" + +# the argument value "True" that is part of "b=True" +# is "value" scope +def func(b=True, c=True): + # the returned value "1" that is part of "return 1" + # is "value" scope + if b is True: + return 1 + else: + return 0 + +# the argument value "False" that is part of "b=False" +# is "value" scope +func(b=False) +# but here, there is no "value" scope for "False" since no argument +func(False) + +# both "1.5" and "25" are "value" scope +val = 1.5 +val /= 25 + +val1, val2 = 0, 5 +val1 = 0, val2 = 5 +val1 = val2 + +def my_funk(value: str) -> str: + print(value) + +def my_funk(value: str = "hello", other: bool=False) -> str: + print(value) + +# we can say "change every value" to allow modifying all the values in one go +def foo(): + a = 0 + b = 1 + c = 2 + +# But we don't support outside of a function yet +a = 0 +b = 1 +c = 2 + +# values of a Python "dictionary" are "value" scope +# we can say "chuck every value" to convert the dict into a set +d1 = {"a": 1, "b": 2, "c": 3} + +_ = {value: key for (key, value) in d1.items()} + +# complex ones +_ = {{"a": 1, "b": 2, "c": 3}: 1, d1: 2} +_ = {{1, 2, 3}: 1, {2, 3, 4}: 2} + +# we don't want the access to a a Python "dictionary" +# value to be of "value" scope so we have it here +# to be sure we ignore it +d1["a"] + +value = "hello world" diff --git a/packages/cursorless-engine/src/languages/python.ts b/packages/cursorless-engine/src/languages/python.ts index d20bea22f5..4ee79dd2cb 100644 --- a/packages/cursorless-engine/src/languages/python.ts +++ b/packages/cursorless-engine/src/languages/python.ts @@ -80,28 +80,6 @@ const nodeMatchers: Partial< "parameters.identifier!", "*[name]", ], - value: cascadingMatcher( - leadingMatcher( - ["assignment[right]", "augmented_assignment[right]", "~subscript[value]"], - [ - ":", - "=", - "+=", - "-=", - "*=", - "/=", - "%=", - "//=", - "**=", - "&=", - "|=", - "^=", - "<<=", - ">>=", - ], - ), - patternMatcher("return_statement.~return!"), - ), argumentOrParameter: cascadingMatcher( argumentMatcher("parameters", "argument_list"), matcher(patternFinder("call.generator_expression!"), childRangeSelector()), diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeValue.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeValue.yml new file mode 100644 index 0000000000..927da9ad05 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeValue.yml @@ -0,0 +1,27 @@ +languageId: python +command: + version: 6 + spokenForm: change value + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: value} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + if (x := 0) < 1: + pass + selections: + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} + marks: {} +finalState: + documentContents: |- + if (x := ) < 1: + pass + selections: + - anchor: {line: 0, character: 9} + active: {line: 0, character: 9} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeValue2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeValue2.yml new file mode 100644 index 0000000000..8c766129bc --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeValue2.yml @@ -0,0 +1,29 @@ +languageId: python +command: + version: 6 + spokenForm: change value + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: value} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + match aaa: + case {"bbb": ccc}: + pass + selections: + - anchor: {line: 1, character: 10} + active: {line: 1, character: 10} + marks: {} +finalState: + documentContents: |- + match aaa: + case {"bbb": }: + pass + selections: + - anchor: {line: 1, character: 17} + active: {line: 1, character: 17} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/chuckValue4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/chuckValue4.yml new file mode 100644 index 0000000000..4841de5b97 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/chuckValue4.yml @@ -0,0 +1,27 @@ +languageId: python +command: + version: 6 + spokenForm: chuck value + action: + name: remove + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: value} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + if (x := 0) < 1: + pass + selections: + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} + marks: {} +finalState: + documentContents: |- + if (x) < 1: + pass + selections: + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/chuckValue5.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/chuckValue5.yml new file mode 100644 index 0000000000..6bd67dc98e --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/chuckValue5.yml @@ -0,0 +1,29 @@ +languageId: python +command: + version: 6 + spokenForm: chuck value + action: + name: remove + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: value} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + match aaa: + case {"bbb": ccc}: + pass + selections: + - anchor: {line: 1, character: 10} + active: {line: 1, character: 10} + marks: {} +finalState: + documentContents: |- + match aaa: + case {"bbb"}: + pass + selections: + - anchor: {line: 1, character: 10} + active: {line: 1, character: 10} diff --git a/pyproject.toml b/pyproject.toml index a345db38d9..b1327e6966 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,8 +1,9 @@ [tool.black] target-version = ["py39"] +force-exclude = ["vendor", "data/playground"] [tool.ruff] select = ["E", "F", "C4", "I001", "UP", "SIM"] ignore = ["E501", "SIM105", "UP035"] target-version = "py39" -extend-exclude = ["vendor"] +extend-exclude = ["vendor", "data/playground/**/*.py"] diff --git a/queries/python.scm b/queries/python.scm index 7566194e9a..094f916174 100644 --- a/queries/python.scm +++ b/queries/python.scm @@ -27,6 +27,58 @@ (with_statement) ] @statement +;;!! a = 25 +;;! ^^ +;;! xxxxx +;;! ------ +(assignment + (_) @_.leading.start.endOf + . + right: (_) @value @_.leading.end.startOf +) @_.domain + +;;!! a /= 25 +;;! ^^ +;;! xxxxxx +;;! ------- +(augmented_assignment + (_) @_.leading.start.endOf + . + right: (_) @value @_.leading.end.startOf +) @_.domain + +;;!! d = {"a": 1234} +;;! ^^^^ +;;! xxxxxx +;;! --------- +;;!! {value: key for (key, value) in d1.items()} +;;! ^^^ +;;! xxxxx +;;! ---------- +;;!! def func(value: str = ""): +;;! ^^ +;;! xxxxx +;;! --------------- +( + (_ + (_) @_.leading.start.endOf + . + value: (_) @value @_.leading.end.startOf + ) @_.domain + (#not-type? @_.domain subscript) +) + +;;!! return 1 +;;! ^ +;;! xx +;;! -------- +;; +;; NOTE: in tree-sitter, both "return" and the "1" are children of `return_statement` +;; but "return" is anonymous whereas "1" is named node, so no need to exclude explicitly +(return_statement + (_) @value +) @_.domain + (comment) @comment @textFragment (string @@ -77,6 +129,26 @@ (module) @statement.iteration (module) @namedFunction.iteration @functionName.iteration (class_definition) @namedFunction.iteration @functionName.iteration -(_ - body: (_) @statement.iteration + +;;!! def foo(): +;;!! a = 0 +;;! <***** +;;!! b = 1 +;;! ***** +;;!! c = 2 +;;! *****> +(block) @statement.iteration @value.iteration + +;;!! {"a": 1, "b": 2, "c": 3} +;;! ********************** +(dictionary + "{" @value.iteration.start.endOf + "}" @value.iteration.end.startOf +) + +;;!! def func(a=0, b=1): +;;! ******** +(parameters + "(" @value.iteration.start.endOf + ")" @value.iteration.end.startOf ) From d0f01ef3aad8e1bb0379010f4bc39885cecae2ee Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 10 Sep 2023 21:10:33 +0200 Subject: [PATCH 21/54] Added text insertion action to Cursorless public api (#1875) Fixes #1754 ## Checklist - [x] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [x] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [/] I have not broken the cheatsheet --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- .../src/cursorless_test.talon | 4 ++ cursorless-talon/src/actions/actions.py | 17 +++++++- cursorless-talon/src/actions/homophones.py | 4 +- cursorless-talon/src/actions/reformat.py | 5 ++- cursorless-talon/src/actions/replace.py | 8 ++-- docs/user/customization.md | 3 ++ .../src/test/fixtures/talonApi.fixture.ts | 43 +++++++++++++++++-- 7 files changed, 73 insertions(+), 11 deletions(-) diff --git a/cursorless-talon-dev/src/cursorless_test.talon b/cursorless-talon-dev/src/cursorless_test.talon index 561abe24b6..03250a3251 100644 --- a/cursorless-talon-dev/src/cursorless_test.talon +++ b/cursorless-talon-dev/src/cursorless_test.talon @@ -7,6 +7,10 @@ test api command : user.cursorless_command("setSelection", cursorless_target) test api command bring : user.cursorless_command("replaceWithTarget", cursorless_target) +test api insert : + user.cursorless_insert(cursorless_destination, word) +test api insert and : + user.cursorless_insert(cursorless_destination, word_list) test api insert snippet: user.cursorless_insert_snippet("Hello, $foo! My name is $bar!") test api insert snippet by name: diff --git a/cursorless-talon/src/actions/actions.py b/cursorless-talon/src/actions/actions.py index b157ba3e0b..0fb6ef4dbb 100644 --- a/cursorless-talon/src/actions/actions.py +++ b/cursorless-talon/src/actions/actions.py @@ -1,10 +1,17 @@ +from typing import Union + from talon import Module, actions -from ..targets.target_types import CursorlessTarget, ImplicitDestination +from ..targets.target_types import ( + CursorlessDestination, + CursorlessTarget, + ImplicitDestination, +) from .bring_move import BringMoveTargets from .call import cursorless_call_action from .execute_command import cursorless_execute_command_action from .homophones import cursorless_homophones_action +from .replace import cursorless_replace_action mod = Module() @@ -111,6 +118,14 @@ def cursorless_ide_command(command_id: str, target: CursorlessTarget): """Perform ide command on cursorless target""" return cursorless_execute_command_action(command_id, target) + def cursorless_insert( + destination: CursorlessDestination, text: Union[str, list[str]] + ): + """Perform text insertion on Cursorless destination""" + if isinstance(text, str): + text = [text] + cursorless_replace_action(destination, text) + def private_cursorless_action_or_ide_command( instruction: dict, target: CursorlessTarget ): diff --git a/cursorless-talon/src/actions/homophones.py b/cursorless-talon/src/actions/homophones.py index a462038071..3b7a7408d7 100644 --- a/cursorless-talon/src/actions/homophones.py +++ b/cursorless-talon/src/actions/homophones.py @@ -1,5 +1,6 @@ from talon import actions, app +from ..targets.target_types import PrimitiveDestination from .get_text import cursorless_get_text_action from .replace import cursorless_replace_action @@ -12,7 +13,8 @@ def cursorless_homophones_action(target: dict): except LookupError as e: app.notify(str(e)) return - cursorless_replace_action(target, updated_texts) + destination = PrimitiveDestination("to", target) + cursorless_replace_action(destination, updated_texts) def get_next_homophone(word: str): diff --git a/cursorless-talon/src/actions/reformat.py b/cursorless-talon/src/actions/reformat.py index 12a142a35f..ce2b89d427 100644 --- a/cursorless-talon/src/actions/reformat.py +++ b/cursorless-talon/src/actions/reformat.py @@ -1,6 +1,6 @@ from talon import Module, actions -from ..targets.target_types import CursorlessTarget +from ..targets.target_types import CursorlessTarget, PrimitiveDestination from .get_text import cursorless_get_text_action from .replace import cursorless_replace_action @@ -15,4 +15,5 @@ def private_cursorless_reformat(target: CursorlessTarget, formatters: str): """Execute Cursorless reformat action. Reformat target with formatter""" texts = cursorless_get_text_action(target, show_decorations=False) updated_texts = [actions.user.reformat_text(text, formatters) for text in texts] - cursorless_replace_action(target, updated_texts) + destination = PrimitiveDestination("to", target) + cursorless_replace_action(destination, updated_texts) diff --git a/cursorless-talon/src/actions/replace.py b/cursorless-talon/src/actions/replace.py index 10e92dd626..4067eb1f2b 100644 --- a/cursorless-talon/src/actions/replace.py +++ b/cursorless-talon/src/actions/replace.py @@ -1,14 +1,16 @@ from talon import actions -from ..targets.target_types import CursorlessTarget, PrimitiveDestination +from ..targets.target_types import CursorlessDestination -def cursorless_replace_action(target: CursorlessTarget, replace_with: list[str]): +def cursorless_replace_action( + destination: CursorlessDestination, replace_with: list[str] +): """Execute Cursorless replace action. Replace targets with texts""" actions.user.private_cursorless_command_and_wait( { "name": "replace", "replaceWith": replace_with, - "destination": PrimitiveDestination("to", target), + "destination": destination, } ) diff --git a/docs/user/customization.md b/docs/user/customization.md index d78e321b65..5fda1a7720 100644 --- a/docs/user/customization.md +++ b/docs/user/customization.md @@ -118,6 +118,9 @@ Cursorless exposes a couple talon actions and captures that you can use to defin - `user.cursorless_ide_command(command_id: str, target: cursorless_target)`: Performs a built-in IDE command on the given target eg: `user.cursorless_ide_command("editor.action.addCommentLine", cursorless_target)` +- `user.cursorless_insert(destination: CursorlessDestination, text: Union[str, List[str]])`: + Insert text at destination. + eg: `user.cursorless_insert(cursorless_destination, "hello")` #### Snippet actions diff --git a/packages/cursorless-engine/src/test/fixtures/talonApi.fixture.ts b/packages/cursorless-engine/src/test/fixtures/talonApi.fixture.ts index 7e8f093fbb..1ca8b892fd 100644 --- a/packages/cursorless-engine/src/test/fixtures/talonApi.fixture.ts +++ b/packages/cursorless-engine/src/test/fixtures/talonApi.fixture.ts @@ -1,4 +1,7 @@ -import { ActionDescriptor } from "@cursorless/common"; +import { + ActionDescriptor, + PartialPrimitiveTargetDescriptor, +} from "@cursorless/common"; import { spokenFormTest } from "./spokenFormTest"; // See cursorless-talon-dev/src/cursorless_test.talon @@ -11,11 +14,29 @@ const setSelectionAction: ActionDescriptor = { }; const replaceWithTargetAction: ActionDescriptor = { name: "replaceWithTarget", - source: { + source: decoratedPrimitiveTarget("a"), + destination: { type: "implicit" }, +}; +const insertSingleWordTargetAction: ActionDescriptor = { + name: "replace", + destination: { type: "primitive", - mark: { type: "decoratedSymbol", symbolColor: "default", character: "a" }, + insertionMode: "to", + target: decoratedPrimitiveTarget("a"), }, - destination: { type: "implicit" }, + replaceWith: ["hello"], +}; +const insertMultipleWordsTargetAction: ActionDescriptor = { + name: "replace", + destination: { + type: "primitive", + insertionMode: "after", + target: { + type: "list", + elements: [decoratedPrimitiveTarget("a"), decoratedPrimitiveTarget("b")], + }, + }, + replaceWith: ["hello", "world"], }; const insertSnippetAction: ActionDescriptor = { name: "insertSnippet", @@ -66,6 +87,11 @@ const wrapWithSnippetByNameAction: ActionDescriptor = { export const talonApiFixture = [ spokenFormTest("test api command this", setSelectionAction), spokenFormTest("test api command bring air", replaceWithTargetAction), + spokenFormTest("test api insert hello to air", insertSingleWordTargetAction), + spokenFormTest( + "test api insert hello and world after air and bat", + insertMultipleWordsTargetAction, + ), spokenFormTest("test api insert snippet", insertSnippetAction), spokenFormTest("test api insert snippet by name", insertSnippetByNameAction), spokenFormTest("test api wrap with snippet this", wrapWithSnippetAction), @@ -74,3 +100,12 @@ export const talonApiFixture = [ wrapWithSnippetByNameAction, ), ]; + +function decoratedPrimitiveTarget( + character: string, +): PartialPrimitiveTargetDescriptor { + return { + type: "primitive", + mark: { type: "decoratedSymbol", symbolColor: "default", character }, + }; +} From 6ec84adced012e731a232cba46c7652177b94140 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Mon, 11 Sep 2023 19:23:48 +0200 Subject: [PATCH 22/54] Added insert action changelog (#1886) ## Checklist - [/] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [x] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [/] I have not broken the cheatsheet --------- Co-authored-by: Pokey Rule <755842+pokey@users.noreply.github.com> --- changelog/2023-09-addedInsertPythonAction.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 changelog/2023-09-addedInsertPythonAction.md diff --git a/changelog/2023-09-addedInsertPythonAction.md b/changelog/2023-09-addedInsertPythonAction.md new file mode 100644 index 0000000000..410432c03b --- /dev/null +++ b/changelog/2023-09-addedInsertPythonAction.md @@ -0,0 +1,7 @@ +--- +tags: [enhancement, talon] +mergeDate: 2023-09-10 +pullRequest: 1875 +--- + +- Added `cursorless_insert` action to the public Talon api. This api enables you to define custom grammars for cursorless text insertion. See the [talon-side api docs](https://www.cursorless.org/docs/user/customization/#public-talon-actions) for more From 2b1bf99dc6a66bd5d01ebce900770417a5d19ef9 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Tue, 12 Sep 2023 08:02:28 -0700 Subject: [PATCH 23/54] add #log! query predicate (#1890) This has proved useful as I struggle with treesitter queries. :) ## Checklist - [-] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [-] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [-] I have not broken the cheatsheet --- .../TreeSitterQuery/queryPredicateOperators.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts index e1647f5f1e..c812fbd09d 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts @@ -161,6 +161,19 @@ class AllowMultiple extends QueryPredicateOperator { } } +/** + * A predicate operator that logs a node, for debugging. + */ +class Log extends QueryPredicateOperator { + name = "log!" as const; + schema = z.tuple([q.node]); + + run(nodeInfo: MutableQueryCapture) { + console.log(`#log!: ${nodeInfo.name}@${nodeInfo.range}`); + return true; + } +} + /** * A predicate operator that sets the insertion delimiter of the match. For * example, `(#insertion-delimiter! @foo ", ")` will set the insertion delimiter @@ -178,6 +191,7 @@ class InsertionDelimiter extends QueryPredicateOperator { } export const queryPredicateOperators = [ + new Log(), new NotType(), new NotEmpty(), new NotParentType(), From 99cf0307da94ad8c5c3b457d13dea7994503862b Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Tue, 12 Sep 2023 09:13:52 -0700 Subject: [PATCH 24/54] improve treesitter query captureStartEnd error messages (#1887) These used to print things like "[Object object]". They are interface types, so we can't simply add a toString method. ## Checklist - [-] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [-] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [-] I have not broken the cheatsheet --- .../src/languages/TreeSitterQuery/checkCaptureStartEnd.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/checkCaptureStartEnd.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/checkCaptureStartEnd.ts index e9b13d22b2..aa80c6bb66 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/checkCaptureStartEnd.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/checkCaptureStartEnd.ts @@ -60,7 +60,9 @@ export function checkCaptureStartEnd( showError( messages, "TreeSitterQuery.checkCaptures.mixRegularStartEnd", - `Please do not mix regular captures and start/end captures: ${captures}`, + `Please do not mix regular captures and start/end captures: ${captures.map( + ({ name, range }) => name + " " + range.toString(), + )}`, ); shownError = true; } @@ -71,7 +73,7 @@ export function checkCaptureStartEnd( messages, "TreeSitterQuery.checkCaptures.duplicate", `A capture with the same name may only appear once in a single pattern: ${captures.map( - ({ name }) => name, + ({ name, range }) => name + " " + range.toString(), )}`, ); shownError = true; From 00d596a7a66ecbfb8e3edd4d1b2fc52abefcd7ed Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 12 Sep 2023 21:13:39 +0200 Subject: [PATCH 25/54] Support generic scoped types for java (#1870) Fixes #1828 ## Checklist - [x] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [/] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [/] I have not broken the cheatsheet --- .../cursorless-engine/src/languages/java.ts | 1 + .../recorded/languages/java/changeType.yml | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/java/changeType.yml diff --git a/packages/cursorless-engine/src/languages/java.ts b/packages/cursorless-engine/src/languages/java.ts index 240dd0d3ce..1be9e7909e 100644 --- a/packages/cursorless-engine/src/languages/java.ts +++ b/packages/cursorless-engine/src/languages/java.ts @@ -93,6 +93,7 @@ const nodeMatchers: Partial< type: trailingMatcher([ "generic_type.type_arguments.type_identifier", "generic_type.type_identifier", + "generic_type.scoped_type_identifier.type_identifier", "type_identifier", "local_variable_declaration[type]", "array_creation_expression[type]", diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/java/changeType.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/java/changeType.yml new file mode 100644 index 0000000000..16d17eb2b3 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/java/changeType.yml @@ -0,0 +1,37 @@ +languageId: java +command: + version: 6 + spokenForm: change type + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: type} + usePrePhraseSnapshot: true +initialState: + documentContents: | + import java.util.Map; + + public class MyClass { + public void myFunk() { + Map.Entry e = null; + } + } + selections: + - anchor: {line: 4, character: 4} + active: {line: 4, character: 4} + marks: {} +finalState: + documentContents: | + import java.util.Map; + + public class MyClass { + public void myFunk() { + e = null; + } + } + selections: + - anchor: {line: 4, character: 4} + active: {line: 4, character: 4} From 072443adf33946b6074e31e075eb24e45df90160 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Wed, 13 Sep 2023 13:23:40 +0200 Subject: [PATCH 26/54] Updated source and that mark for replace action (#1884) Fixes #1883 ## Checklist - [x] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [/] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [/] I have not broken the cheatsheet --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- .../cursorless-engine/src/actions/Replace.ts | 60 +++++++++++++------ .../cursorless-engine/src/actions/Sort.ts | 4 +- .../recorded/actions/placeHelloAfterAir.yml | 43 +++++++++++++ .../recorded/actions/placeHelloToFine.yml | 43 +++++++++++++ 4 files changed, 131 insertions(+), 19 deletions(-) create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/placeHelloAfterAir.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/placeHelloToFine.yml diff --git a/packages/cursorless-engine/src/actions/Replace.ts b/packages/cursorless-engine/src/actions/Replace.ts index a4b7a9b192..9b0fe5b6b1 100644 --- a/packages/cursorless-engine/src/actions/Replace.ts +++ b/packages/cursorless-engine/src/actions/Replace.ts @@ -1,9 +1,14 @@ -import { FlashStyle, ReplaceWith } from "@cursorless/common"; -import { flatten, zip } from "lodash"; +import { + FlashStyle, + RangeExpansionBehavior, + ReplaceWith, +} from "@cursorless/common"; +import { zip } from "lodash"; import { RangeUpdater } from "../core/updateSelections/RangeUpdater"; -import { performEditsAndUpdateSelections } from "../core/updateSelections/updateSelections"; +import { performEditsAndUpdateSelectionsWithBehavior } from "../core/updateSelections/updateSelections"; import { ide } from "../singletons/ide.singleton"; -import { Destination } from "../typings/target.types"; +import { SelectionWithEditor } from "../typings/Types"; +import { Destination, Target } from "../typings/target.types"; import { flashTargets, runForEachEditor } from "../util/targetUtils"; import { ActionReturnValue } from "./actions.types"; @@ -47,30 +52,49 @@ export default class Replace { } const edits = zip(destinations, texts).map(([destination, text]) => ({ - edit: destination!.constructChangeEdit(text!), editor: destination!.editor, + target: destination!.target, + edit: destination!.constructChangeEdit(text!), })); - const thatMark = flatten( - await runForEachEditor( - edits, - (edit) => edit.editor, - async (editor, edits) => { - const [updatedSelections] = await performEditsAndUpdateSelections( + const sourceTargets: Target[] = []; + const thatSelections: SelectionWithEditor[] = []; + + await runForEachEditor( + edits, + (edit) => edit.editor, + async (editor, edits) => { + const contentSelections = { + selections: edits.map(({ target }) => target.contentSelection), + }; + const editSelections = { + selections: edits.map(({ edit }) => edit.range.toSelection(false)), + rangeBehavior: RangeExpansionBehavior.openOpen, + }; + + const [updatedContentSelections, updatedEditSelections] = + await performEditsAndUpdateSelectionsWithBehavior( this.rangeUpdater, ide().getEditableTextEditor(editor), edits.map(({ edit }) => edit), - [destinations.map((destination) => destination.contentSelection)], + [contentSelections, editSelections], ); - return updatedSelections.map((selection) => ({ + for (const [edit, selection] of zip(edits, updatedContentSelections)) { + sourceTargets.push(edit!.target.withContentRange(selection!)); + } + + for (const [edit, selection] of zip(edits, updatedEditSelections)) { + thatSelections.push({ editor, - selection, - })); - }, - ), + selection: edit!.edit + .updateRange(selection!) + .toSelection(selection!.isReversed), + }); + } + }, ); - return { thatSelections: thatMark }; + return { sourceTargets, thatSelections }; } } diff --git a/packages/cursorless-engine/src/actions/Sort.ts b/packages/cursorless-engine/src/actions/Sort.ts index b5c1423a00..e7c823a59e 100644 --- a/packages/cursorless-engine/src/actions/Sort.ts +++ b/packages/cursorless-engine/src/actions/Sort.ts @@ -35,10 +35,12 @@ abstract class SortBase implements SimpleAction { const sortedTexts = this.sortTexts(unsortedTexts); - return this.actions.replace.run( + const { thatSelections } = await this.actions.replace.run( sortedTargets.map((target) => target.toDestination("to")), sortedTexts, ); + + return { thatSelections }; } } diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/placeHelloAfterAir.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/placeHelloAfterAir.yml new file mode 100644 index 0000000000..2b54fc0599 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/placeHelloAfterAir.yml @@ -0,0 +1,43 @@ +languageId: plaintext +command: + version: 6 + spokenForm: place hello after air + action: + name: replace + replaceWith: [hello] + destination: + type: primitive + insertionMode: after + target: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: a} + usePrePhraseSnapshot: true +spokenFormError: Action 'replace' +initialState: + documentContents: aaa + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: + default.a: + start: {line: 0, character: 0} + end: {line: 0, character: 3} +finalState: + documentContents: aaa hello + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + thatMark: + - type: UntypedTarget + contentRange: + start: {line: 0, character: 4} + end: {line: 0, character: 9} + isReversed: false + hasExplicitRange: true + sourceMark: + - type: UntypedTarget + contentRange: + start: {line: 0, character: 0} + end: {line: 0, character: 3} + isReversed: false + hasExplicitRange: false diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/placeHelloToFine.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/placeHelloToFine.yml new file mode 100644 index 0000000000..62674c6483 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/placeHelloToFine.yml @@ -0,0 +1,43 @@ +languageId: plaintext +command: + version: 6 + spokenForm: place hello to fine + action: + name: replace + replaceWith: [hello] + destination: + type: primitive + insertionMode: to + target: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: f} + usePrePhraseSnapshot: true +spokenFormError: Action 'replace' +initialState: + documentContents: foo + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: + default.f: + start: {line: 0, character: 0} + end: {line: 0, character: 3} +finalState: + documentContents: hello + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + thatMark: + - type: UntypedTarget + contentRange: + start: {line: 0, character: 0} + end: {line: 0, character: 5} + isReversed: false + hasExplicitRange: true + sourceMark: + - type: UntypedTarget + contentRange: + start: {line: 0, character: 0} + end: {line: 0, character: 5} + isReversed: false + hasExplicitRange: false From dbe101f5ced302597400b1b969b9d5723b46fcef Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 14 Sep 2023 15:50:42 +0200 Subject: [PATCH 27/54] Setting for hat shape overrides (#1853) ![image](https://github.com/cursorless-dev/cursorless/assets/3511326/c4dfb684-4fc5-4337-ba07-25753359a602) * added hidden setting `cursorless.private.hatShapesDir` * Also made svg parser more robust ## Checklist - [/] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [/] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [/] I have not broken the cheatsheet --------- Co-authored-by: Pokey Rule <755842+pokey@users.noreply.github.com> --- changelog/2023-09-addedInsertPythonAction.md | 4 +- packages/common/src/util/walkAsync.ts | 15 +- .../cursorless-engine/src/core/Snippets.ts | 6 +- packages/cursorless-vscode/package.json | 2 +- .../src/ide/vscode/hats/VscodeHatRenderer.ts | 175 +++++++++++++++--- .../src/ide/vscode/hats/VscodeHats.ts | 2 +- 6 files changed, 164 insertions(+), 40 deletions(-) diff --git a/changelog/2023-09-addedInsertPythonAction.md b/changelog/2023-09-addedInsertPythonAction.md index 410432c03b..417b0d0c08 100644 --- a/changelog/2023-09-addedInsertPythonAction.md +++ b/changelog/2023-09-addedInsertPythonAction.md @@ -1,7 +1,7 @@ --- tags: [enhancement, talon] -mergeDate: 2023-09-10 pullRequest: 1875 +mergeDate: 2023-09-10 --- -- Added `cursorless_insert` action to the public Talon api. This api enables you to define custom grammars for cursorless text insertion. See the [talon-side api docs](https://www.cursorless.org/docs/user/customization/#public-talon-actions) for more +- Added `cursorless_insert` action to the public Talon api. This api enables you to define custom grammars for Cursorless text insertion. See the [talon-side api docs](https://www.cursorless.org/docs/user/customization/#public-talon-actions) for more diff --git a/packages/common/src/util/walkAsync.ts b/packages/common/src/util/walkAsync.ts index 2094141523..a42600fd7f 100644 --- a/packages/common/src/util/walkAsync.ts +++ b/packages/common/src/util/walkAsync.ts @@ -6,13 +6,16 @@ import { flatten } from "lodash"; * Note: Returns full paths * Based on https://gist.github.com/kethinov/6658166#gistcomment-1941504 * @param dir - * @param filelist + * @param fileEnding If defined, only return files ending with this string. Eg `.txt` * @returns */ -export const walkFiles = async (dir: string): Promise => { +export const walkFiles = async ( + dir: string, + fileEnding?: string, +): Promise => { const dirEntries = await readdir(dir, { withFileTypes: true }); - return flatten( + const files = flatten( await Promise.all( dirEntries.map(async (dirent) => { const filePath = path.join(dir, dirent.name); @@ -20,4 +23,10 @@ export const walkFiles = async (dir: string): Promise => { }), ), ); + + if (fileEnding != null) { + return files.filter((file) => file.endsWith(fileEnding)); + } + + return files; }; diff --git a/packages/cursorless-engine/src/core/Snippets.ts b/packages/cursorless-engine/src/core/Snippets.ts index d93e61e83f..373bdccf2f 100644 --- a/packages/cursorless-engine/src/core/Snippets.ts +++ b/packages/cursorless-engine/src/core/Snippets.ts @@ -236,8 +236,6 @@ export class Snippets { } } -async function getSnippetPaths(snippetsDir: string) { - return (await walkFiles(snippetsDir)).filter((path) => - path.endsWith(CURSORLESS_SNIPPETS_SUFFIX), - ); +function getSnippetPaths(snippetsDir: string) { + return walkFiles(snippetsDir, CURSORLESS_SNIPPETS_SUFFIX); } diff --git a/packages/cursorless-vscode/package.json b/packages/cursorless-vscode/package.json index e5ac5803a2..584de90030 100644 --- a/packages/cursorless-vscode/package.json +++ b/packages/cursorless-vscode/package.json @@ -838,7 +838,7 @@ } }, "cursorless.experimental.snippetsDir": { - "description": "Directory containing snippets for use in cursorless", + "description": "Directory containing snippets for use in Cursorless", "type": "string" }, "cursorless.experimental.keyboard.modal.keybindings.actions": { diff --git a/packages/cursorless-vscode/src/ide/vscode/hats/VscodeHatRenderer.ts b/packages/cursorless-vscode/src/ide/vscode/hats/VscodeHatRenderer.ts index 315f5ee6c9..9159998e64 100644 --- a/packages/cursorless-vscode/src/ide/vscode/hats/VscodeHatRenderer.ts +++ b/packages/cursorless-vscode/src/ide/vscode/hats/VscodeHatRenderer.ts @@ -1,20 +1,27 @@ -import { readFileSync } from "fs"; +import { + Listener, + Notifier, + PathChangeListener, + walkFiles, +} from "@cursorless/common"; import { cloneDeep, isEqual } from "lodash"; -import { join } from "path"; +import * as fs from "node:fs"; +import * as path from "node:path"; import * as vscode from "vscode"; +import VscodeEnabledHatStyleManager, { + ExtendedHatStyleMap, +} from "../VscodeEnabledHatStyleManager"; +import { HAT_SHAPES, HatShape, VscodeHatStyleName } from "../hatStyles.types"; +import { FontMeasurements } from "./FontMeasurements"; import getHatThemeColors from "./getHatThemeColors"; import { - defaultShapeAdjustments, DEFAULT_HAT_HEIGHT_EM, DEFAULT_VERTICAL_OFFSET_EM, IndividualHatAdjustmentMap, + defaultShapeAdjustments, } from "./shapeAdjustments"; -import { Listener, Notifier } from "@cursorless/common"; -import { FontMeasurements } from "./FontMeasurements"; -import { HatShape, HAT_SHAPES, VscodeHatStyleName } from "../hatStyles.types"; -import VscodeEnabledHatStyleManager, { - ExtendedHatStyleMap, -} from "../VscodeEnabledHatStyleManager"; + +const CURSORLESS_HAT_SHAPES_SUFFIX = ".svg"; type HatDecorationMap = Partial< Record @@ -39,11 +46,24 @@ const hatConfigSections = [ * hats. The decision about which hat styles should be available is up to * {@link VscodeEnabledHatStyles} */ + +const SETTING_SECTION_HAT_SHAPES_DIR = "cursorless.private"; +const SETTING_NAME_HAT_SHAPES_DIR = "hatShapesDir"; +const hatShapesDirSettingId = `${SETTING_SECTION_HAT_SHAPES_DIR}.${SETTING_NAME_HAT_SHAPES_DIR}`; + +interface SvgInfo { + svg: string; + svgHeightPx: number; + svgWidthPx: number; +} + export default class VscodeHatRenderer { private decorationMap!: HatDecorationMap; private disposables: vscode.Disposable[] = []; private notifier: Notifier<[]> = new Notifier(); private lastSeenEnabledHatStyles: ExtendedHatStyleMap = {}; + private hatsDirWatcherDisposable?: vscode.Disposable; + private hatShapeOverrides: Record = {}; constructor( private extensionContext: vscode.ExtensionContext, @@ -57,7 +77,9 @@ export default class VscodeHatRenderer { this.disposables.push( vscode.workspace.onDidChangeConfiguration( async ({ affectsConfiguration }) => { - if ( + if (affectsConfiguration(hatShapesDirSettingId)) { + await this.updateHatsDirWatcher(); + } else if ( hatConfigSections.some((section) => affectsConfiguration(section)) ) { await this.recomputeDecorations(); @@ -88,6 +110,7 @@ export default class VscodeHatRenderer { async init() { await this.constructDecorations(); + await this.updateHatsDirWatcher(); } /** @@ -99,6 +122,52 @@ export default class VscodeHatRenderer { return this.decorationMap[hatStyle]; } + private async updateHatsDirWatcher() { + this.hatsDirWatcherDisposable?.dispose(); + + const hatsDir = vscode.workspace + .getConfiguration(SETTING_SECTION_HAT_SHAPES_DIR) + .get(SETTING_NAME_HAT_SHAPES_DIR)!; + + if (hatsDir) { + await this.updateShapeOverrides(hatsDir); + + if (fs.existsSync(hatsDir)) { + this.hatsDirWatcherDisposable = watchDir(hatsDir, () => + this.updateShapeOverrides(hatsDir), + ); + } + } else { + this.hatShapeOverrides = {}; + await this.recomputeDecorations(); + } + } + + private async updateShapeOverrides(hatShapesDir: string) { + this.hatShapeOverrides = {}; + const files = await this.getHatShapePaths(hatShapesDir); + + for (const file of files) { + const name = path.basename(file, CURSORLESS_HAT_SHAPES_SUFFIX); + this.hatShapeOverrides[name] = file; + } + + await this.recomputeDecorations(); + } + + private async getHatShapePaths(hatShapesDir: string) { + try { + return await walkFiles(hatShapesDir, CURSORLESS_HAT_SHAPES_SUFFIX); + } catch (error) { + void vscode.window.showErrorMessage( + `Error with cursorless hat shapes dir "${hatShapesDir}": ${ + (error as Error).message + }`, + ); + return []; + } + } + private destroyDecorations() { Object.values(this.decorationMap).forEach((decoration) => { decoration.dispose(); @@ -160,7 +229,16 @@ export default class VscodeHatRenderer { this.decorationMap = Object.fromEntries( Object.entries(this.enabledHatStyles.hatStyleMap).map( ([styleName, { color, shape }]) => { - const { svg, svgWidthPx, svgHeightPx } = hatSvgMap[shape]; + const svgInfo = hatSvgMap[shape]; + + if (svgInfo == null) { + return [ + styleName, + vscode.window.createTextEditorDecorationType({}), + ]; + } + + const { svg, svgWidthPx, svgHeightPx } = svgInfo; const { light, dark } = getHatThemeColors(color); @@ -194,17 +272,36 @@ export default class VscodeHatRenderer { ); } - private constructColoredSvgDataUri(originalSvg: string, color: string) { + private checkSvg(shape: HatShape, svg: string) { + let isOk = true; + if ( - originalSvg.match(/fill="[^"]+"/) == null && - originalSvg.match(/fill:[^;]+;/) == null + svg.match(/fill="(?!none)[^"]+"/) == null && + svg.match(/fill:(?!none)[^;]+;/) == null ) { - throw Error("Raw svg doesn't have fill"); + vscode.window.showErrorMessage( + `Raw svg '${shape}' is missing 'fill' property`, + ); + isOk = false; } + const viewBoxMatch = svg.match(/viewBox="([^"]+)"/); + + if (viewBoxMatch == null) { + vscode.window.showErrorMessage( + `Raw svg '${shape}' is missing 'viewBox' property`, + ); + isOk = false; + } + + return isOk; + } + + private constructColoredSvgDataUri(originalSvg: string, color: string) { const svg = originalSvg - .replace(/fill="[^"]+"/, `fill="${color}"`) - .replace(/fill:[^;]+;/, `fill:${color};`); + .replace(/fill="(?!none)[^"]+"/g, `fill="${color}"`) + .replace(/fill:(?!none)[^;]+;/g, `fill:${color};`) + .replace(/\r?\n/g, " "); const encoded = encodeURIComponent(svg); @@ -227,16 +324,22 @@ export default class VscodeHatRenderer { shape: HatShape, scaleFactor: number, hatVerticalOffsetEm: number, - ) { - const iconPath = join( - this.extensionContext.extensionPath, - "images", - "hats", - `${shape}.svg`, - ); - const rawSvg = readFileSync(iconPath, "utf8"); + ): SvgInfo | null { + const iconPath = + this.hatShapeOverrides[shape] ?? + path.join( + this.extensionContext.extensionPath, + "images", + "hats", + `${shape}.svg`, + ); + const rawSvg = fs.readFileSync(iconPath, "utf8"); const { characterWidth, characterHeight, fontSize } = fontMeasurements; + if (!this.checkSvg(shape, rawSvg)) { + return null; + } + const { originalViewBoxHeight, originalViewBoxWidth } = this.getViewBoxDimensions(rawSvg); @@ -289,10 +392,7 @@ export default class VscodeHatRenderer { } private getViewBoxDimensions(rawSvg: string) { - const viewBoxMatch = rawSvg.match(/viewBox="([^"]+)"/); - if (viewBoxMatch == null) { - throw Error("View box not found in svg"); - } + const viewBoxMatch = rawSvg.match(/viewBox="([^"]+)"/)!; const originalViewBoxString = viewBoxMatch[1]; const [_0, _1, originalViewBoxWidthStr, originalViewBoxHeightStr] = @@ -306,6 +406,23 @@ export default class VscodeHatRenderer { dispose() { this.destroyDecorations(); + this.hatsDirWatcherDisposable?.dispose(); this.disposables.forEach(({ dispose }) => dispose()); } } + +function watchDir( + path: string, + onDidChange: PathChangeListener, +): vscode.Disposable { + const hatsDirWatcher = vscode.workspace.createFileSystemWatcher( + new vscode.RelativePattern(path, `**/*${CURSORLESS_HAT_SHAPES_SUFFIX}`), + ); + + return vscode.Disposable.from( + hatsDirWatcher, + hatsDirWatcher.onDidChange(onDidChange), + hatsDirWatcher.onDidCreate(onDidChange), + hatsDirWatcher.onDidDelete(onDidChange), + ); +} diff --git a/packages/cursorless-vscode/src/ide/vscode/hats/VscodeHats.ts b/packages/cursorless-vscode/src/ide/vscode/hats/VscodeHats.ts index df1a0b136b..255a7ab3c7 100644 --- a/packages/cursorless-vscode/src/ide/vscode/hats/VscodeHats.ts +++ b/packages/cursorless-vscode/src/ide/vscode/hats/VscodeHats.ts @@ -15,8 +15,8 @@ import { VscodeHatStyleName } from "../hatStyles.types"; import VscodeEnabledHatStyleManager from "../VscodeEnabledHatStyleManager"; import type { VscodeIDE } from "../VscodeIDE"; import { VscodeTextEditorImpl } from "../VscodeTextEditorImpl"; -import VscodeHatRenderer from "./VscodeHatRenderer"; import { FontMeasurements } from "./FontMeasurements"; +import VscodeHatRenderer from "./VscodeHatRenderer"; export class VscodeHats implements Hats { private enabledHatStyleManager: VscodeEnabledHatStyleManager; From afa3802ed30f45d0d069f2efa1d96b373a507279 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 14 Sep 2023 15:57:53 +0200 Subject: [PATCH 28/54] Reduce extension dependencies (#1891) Remove unnecessary extension dependencies ## Checklist - [/] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [/] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [/] I have not broken the cheatsheet --- packages/common/src/extensionDependencies.ts | 7 +++++-- .../cursorless-vscode/src/scripts/initLaunchSandbox.ts | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/common/src/extensionDependencies.ts b/packages/common/src/extensionDependencies.ts index 08a37aa65c..bf524ae616 100644 --- a/packages/common/src/extensionDependencies.ts +++ b/packages/common/src/extensionDependencies.ts @@ -1,8 +1,11 @@ export const extensionDependencies = [ "pokey.parse-tree", - "ms-toolsai.jupyter", + + // Register necessary file extensions for tests "scalameta.metals", - "ms-python.python", "mrob95.vscode-talonscript", "jrieken.vscode-tree-sitter-query", + + // Necessary for the `drink cell` and `pour cell` tests + "ms-toolsai.jupyter", ]; diff --git a/packages/cursorless-vscode/src/scripts/initLaunchSandbox.ts b/packages/cursorless-vscode/src/scripts/initLaunchSandbox.ts index 5d7343aac3..db5f7591d3 100644 --- a/packages/cursorless-vscode/src/scripts/initLaunchSandbox.ts +++ b/packages/cursorless-vscode/src/scripts/initLaunchSandbox.ts @@ -6,7 +6,7 @@ import { extensionDependencies } from "@cursorless/common"; import * as cp from "child_process"; -const extraExtensions = ["pokey.command-server", "pokey.talon"]; +const extraExtensions = ["pokey.command-server"]; async function main() { try { From 9c7ddfea4f276b3e114e0cf5c0c7f582722d6ba3 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Fri, 15 Sep 2023 06:17:19 -0700 Subject: [PATCH 29/54] cull dead code (#1889) Suggested by some linter in GitHub Actions amidst a bunch of other scrollback. ## Checklist - [-] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [-] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [-] I have not broken the cheatsheet --- .../BaseTreeSitterScopeHandler.ts | 97 +------------------ 1 file changed, 2 insertions(+), 95 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/BaseTreeSitterScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/BaseTreeSitterScopeHandler.ts index 3011807a4e..d59a06d501 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/BaseTreeSitterScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/BaseTreeSitterScopeHandler.ts @@ -1,20 +1,11 @@ -import { - Direction, - Position, - TextDocument, - TextEditor, - showError, -} from "@cursorless/common"; +import { Direction, Position, TextEditor, showError } from "@cursorless/common"; import { uniqWith } from "lodash"; import { TreeSitterQuery } from "../../../../languages/TreeSitterQuery"; import { QueryMatch } from "../../../../languages/TreeSitterQuery/QueryCapture"; import BaseScopeHandler from "../BaseScopeHandler"; import { compareTargetScopes } from "../compareTargetScopes"; import { TargetScope } from "../scope.types"; -import { - ContainmentPolicy, - ScopeIteratorRequirements, -} from "../scopeHandler.types"; +import { ScopeIteratorRequirements } from "../scopeHandler.types"; import { mergeAdjacentBy } from "./mergeAdjacentBy"; import { ide } from "../../../../singletons/ide.singleton"; @@ -105,87 +96,3 @@ export abstract class BaseTreeSitterScopeHandler extends BaseScopeHandler { export interface ExtendedTargetScope extends TargetScope { allowMultiple: boolean; } - -/** - * Constructs a range to pass to {@link Query.matches} to find scopes. Note - * that {@link Query.matches} will only return scopes that have non-empty - * intersection with this range. Also note that the base - * {@link BaseScopeHandler.generateScopes} will filter out any extra scopes - * that we yield, so we don't need to be totally precise. - * - * @returns Range to pass to {@link Query.matches} - */ -function getQuerySearchRange( - document: TextDocument, - position: Position, - direction: Direction, - { - containment, - distalPosition, - allowAdjacentScopes, - }: ScopeIteratorRequirements, -) { - const { start, end } = getQuerySearchRangeCore( - document.offsetAt(position), - document.offsetAt(distalPosition), - direction, - containment, - allowAdjacentScopes, - ); - - return { - start: document.positionAt(start), - end: document.positionAt(end), - }; -} - -/** Helper function for {@link getQuerySearchRange} */ -function getQuerySearchRangeCore( - offset: number, - distalOffset: number, - direction: Direction, - containment: ContainmentPolicy | null, - allowAdjacentScopes: boolean, -) { - /** - * If we allow adjacent scopes, we need to shift some of our offsets by one - * character - */ - const adjacentShift = allowAdjacentScopes ? 1 : 0; - - if (containment === "required") { - // If containment is required, we smear the position left and right by one - // character so that we have a non-empty intersection with any scope that - // touches position. Note that we can skip shifting the initial position - // if we disallow adjacent scopes. - return direction === "forward" - ? { - start: offset - adjacentShift, - end: offset + 1, - } - : { - start: offset - 1, - end: offset + adjacentShift, - }; - } - - // If containment is disallowed, we can shift the position forward by a - // character to avoid matching scopes that touch position. Otherwise, we - // shift the position backward by a character to ensure we get scopes that - // touch position, if we allow adjacent scopes. - const proximalShift = containment === "disallowed" ? 1 : -adjacentShift; - - // FIXME: Don't go all the way to end of document when there is no - // distalPosition? Seems wasteful to query all the way to end of document for - // something like "next funk" Might be better to start smaller and - // exponentially grow - return direction === "forward" - ? { - start: offset + proximalShift, - end: distalOffset + adjacentShift, - } - : { - start: distalOffset - adjacentShift, - end: offset - proximalShift, - }; -} From 1ba333144176242a8dd839fd151a48334d625af2 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Fri, 15 Sep 2023 15:23:19 +0200 Subject: [PATCH 30/54] Added ts `as` and `satisfies` support for `type` scope (#1871) Fixes #1155 ## Checklist - [x] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [/] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [/] I have not broken the cheatsheet --- .../src/languages/typescript.ts | 11 ++++++++- .../languages/typescript/changeType.yml | 23 +++++++++++++++++++ .../languages/typescript/changeType2.yml | 23 +++++++++++++++++++ .../languages/typescript/changeType3.yml | 23 +++++++++++++++++++ .../languages/typescript/changeType4.yml | 23 +++++++++++++++++++ .../languages/typescript/changeType5.yml | 23 +++++++++++++++++++ .../languages/typescript/changeType6.yml | 23 +++++++++++++++++++ 7 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType3.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType4.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType5.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType6.yml diff --git a/packages/cursorless-engine/src/languages/typescript.ts b/packages/cursorless-engine/src/languages/typescript.ts index 0ffd960729..8498028e4d 100644 --- a/packages/cursorless-engine/src/languages/typescript.ts +++ b/packages/cursorless-engine/src/languages/typescript.ts @@ -231,10 +231,19 @@ const nodeMatchers: Partial< // Typed parameters, properties, and functions typeMatcher(), // matcher(findTypeNode, selectWithLeadingDelimiter(":")), - // Type alias/interface declarations patternMatcher( + // Type alias/interface declarations "export_statement?.type_alias_declaration", "export_statement?.interface_declaration", + // as type declarations + "as_expression.generic_type!", + "as_expression.predefined_type!", + // satisfies type declaration + "satisfies_expression.generic_type!", + "satisfies_expression.predefined_type!", + // declaration + "type_assertion.type_arguments.generic_type!", + "type_assertion.type_arguments.predefined_type!", ), ), argumentOrParameter: argumentMatcher("formal_parameters", "arguments"), diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType.yml new file mode 100644 index 0000000000..40df97954d --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType.yml @@ -0,0 +1,23 @@ +languageId: typescript +command: + version: 6 + spokenForm: change type + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: type} + usePrePhraseSnapshot: true +initialState: + documentContents: const value = 2 as number; + selections: + - anchor: {line: 0, character: 19} + active: {line: 0, character: 19} + marks: {} +finalState: + documentContents: const value = 2 as ; + selections: + - anchor: {line: 0, character: 19} + active: {line: 0, character: 19} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType2.yml new file mode 100644 index 0000000000..2b97d0a6f6 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType2.yml @@ -0,0 +1,23 @@ +languageId: typescript +command: + version: 6 + spokenForm: change type + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: type} + usePrePhraseSnapshot: true +initialState: + documentContents: const map = {} as Record; + selections: + - anchor: {line: 0, character: 18} + active: {line: 0, character: 18} + marks: {} +finalState: + documentContents: const map = {} as ; + selections: + - anchor: {line: 0, character: 18} + active: {line: 0, character: 18} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType3.yml new file mode 100644 index 0000000000..43b9687d48 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType3.yml @@ -0,0 +1,23 @@ +languageId: typescript +command: + version: 6 + spokenForm: change type + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: type} + usePrePhraseSnapshot: true +initialState: + documentContents: const value = 2 as const satisfies number; + selections: + - anchor: {line: 0, character: 35} + active: {line: 0, character: 35} + marks: {} +finalState: + documentContents: const value = 2 as const satisfies ; + selections: + - anchor: {line: 0, character: 35} + active: {line: 0, character: 35} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType4.yml new file mode 100644 index 0000000000..b08e288628 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType4.yml @@ -0,0 +1,23 @@ +languageId: typescript +command: + version: 6 + spokenForm: change type + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: type} + usePrePhraseSnapshot: true +initialState: + documentContents: const meta = {} as const satisfies Record; + selections: + - anchor: {line: 0, character: 35} + active: {line: 0, character: 35} + marks: {} +finalState: + documentContents: const meta = {} as const satisfies ; + selections: + - anchor: {line: 0, character: 35} + active: {line: 0, character: 35} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType5.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType5.yml new file mode 100644 index 0000000000..da503d66e9 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType5.yml @@ -0,0 +1,23 @@ +languageId: typescript +command: + version: 6 + spokenForm: change type + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: type} + usePrePhraseSnapshot: true +initialState: + documentContents: const value = 2; + selections: + - anchor: {line: 0, character: 15} + active: {line: 0, character: 15} + marks: {} +finalState: + documentContents: const value = <>2; + selections: + - anchor: {line: 0, character: 15} + active: {line: 0, character: 15} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType6.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType6.yml new file mode 100644 index 0000000000..0c204f6ca3 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType6.yml @@ -0,0 +1,23 @@ +languageId: typescript +command: + version: 6 + spokenForm: change type + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: type} + usePrePhraseSnapshot: true +initialState: + documentContents: const map = >{} + selections: + - anchor: {line: 0, character: 13} + active: {line: 0, character: 13} + marks: {} +finalState: + documentContents: const map = <>{} + selections: + - anchor: {line: 0, character: 13} + active: {line: 0, character: 13} From 76085d10889b828078967b4c4aefe0e930cb12f9 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Fri, 15 Sep 2023 06:27:12 -0700 Subject: [PATCH 31/54] improve Range function signatures and docs (#1896) If the first arg isn't reliably used as start in a constructor, then don't call it that. (This bit me hard.) Call out swapping behavior in with. (So did this.) Make other docs more precise. ## Checklist - [-] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [-] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [-] I have not broken the cheatsheet --- packages/common/src/types/Range.ts | 50 ++++++++++++++---------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/packages/common/src/types/Range.ts b/packages/common/src/types/Range.ts index b0ffe057f3..1230bbc048 100644 --- a/packages/common/src/types/Range.ts +++ b/packages/common/src/types/Range.ts @@ -12,32 +12,27 @@ export class Range { readonly end: Position; /** - * Create a new range from two positions. If `start` is not - * before or equal to `end`, the values will be swapped. + * Create a new range from two positions. + * The earlier of `p1` and `p2` will be used as the start position. * - * @param start A position. - * @param end A position. + * @param p1 A position. + * @param p2 A position. */ - constructor(start: Position, end: Position); + constructor(p1: Position, p2: Position); /** - * Create a new range from number coordinates. It is a shorter equivalent of - * using `new Range(new Position(startLine, startCharacter), new Position(endLine, endCharacter))` + * Create a new range from number coordinates. + * It is equivalent to `new Range(new Position(line1, char1), new Position(line2, char2))` * - * @param startLine A zero-based line value. - * @param startCharacter A zero-based character value. - * @param endLine A zero-based line value. - * @param endCharacter A zero-based character value. + * @param line1 A zero-based line value. + * @param char1 A zero-based character value. + * @param line2 A zero-based line value. + * @param char2 A zero-based character value. */ - constructor( - startLine: number, - startCharacter: number, - endLine: number, - endCharacter: number, - ); + constructor(line1: number, char1: number, line2: number, char2: number); constructor(...args: any[]) { - const [start, end]: [Position, Position] = (() => { + const [p1, p2]: [Position, Position] = (() => { // Arguments are two positions if (args.length === 2) { return args as [Position, Position]; @@ -48,12 +43,12 @@ export class Range { })(); // Ranges are always non-reversed - if (start.isBefore(end)) { - this.start = start; - this.end = end; + if (p1.isBefore(p2)) { + this.start = p1; + this.end = p2; } else { - this.start = end; - this.end = start; + this.start = p2; + this.end = p1; } } @@ -98,8 +93,9 @@ export class Range { } /** - * Intersect `range` with this range and returns a new range or `undefined` - * if the ranges have no overlap. + * Intersect `range` with this range and returns a new range. + * If the ranges are adjacent but non-overlapping, the resulting range is empty. + * If the ranges have no overlap and are not adjacent, it returns `undefined`. * * @param other A range. * @return A range of the greater start and smaller end positions. Will @@ -125,12 +121,12 @@ export class Range { } /** - * Derived a new range from this range. + * Derive a new range from this range. + * If the resulting range has end before start, they are swapped. * * @param start A position that should be used as start. The default value is the {@link Range.start current start}. * @param end A position that should be used as end. The default value is the {@link Range.end current end}. * @return A range derived from this range with the given start and end position. - * If start and end are not different `this` range will be returned. */ public with(start?: Position, end?: Position): Range { return new Range(start ?? this.start, end ?? this.end); From 81a757c18c566035afa10f9d1fbba2b3274bfbb5 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Fri, 15 Sep 2023 15:28:03 +0200 Subject: [PATCH 32/54] Switch scala dependency extension (#1897) `scalameta.metals` includes `scala-lang.scala` that actually adds the language identifier that we need. Switch to it directly to avoid installing more extensions than necessary ## Checklist - [/] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [/] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [/] I have not broken the cheatsheet --- packages/common/src/extensionDependencies.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/common/src/extensionDependencies.ts b/packages/common/src/extensionDependencies.ts index bf524ae616..5139c3b3a7 100644 --- a/packages/common/src/extensionDependencies.ts +++ b/packages/common/src/extensionDependencies.ts @@ -1,10 +1,11 @@ export const extensionDependencies = [ + // Cursorless access to Tree sitter "pokey.parse-tree", - // Register necessary file extensions for tests - "scalameta.metals", - "mrob95.vscode-talonscript", - "jrieken.vscode-tree-sitter-query", + // Register necessary language-IDs for tests + "scala-lang.scala", // scala + "mrob95.vscode-talonscript", // talon + "jrieken.vscode-tree-sitter-query", // scm // Necessary for the `drink cell` and `pour cell` tests "ms-toolsai.jupyter", From 9a1bd9379a314381c2f264c302219069908cab62 Mon Sep 17 00:00:00 2001 From: David Vo Date: Sat, 16 Sep 2023 00:03:40 +1000 Subject: [PATCH 33/54] Type hint Talon cheatsheet get_list (#1850) ## Checklist - [ ] ~I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/)~ - [ ] ~I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet)~ - [x] I have not broken the cheatsheet --- cursorless-talon/src/cheatsheet/get_list.py | 35 +++++++++++++++------ 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/cursorless-talon/src/cheatsheet/get_list.py b/cursorless-talon/src/cheatsheet/get_list.py index 9bf49586a0..001feaa1cc 100644 --- a/cursorless-talon/src/cheatsheet/get_list.py +++ b/cursorless-talon/src/cheatsheet/get_list.py @@ -1,33 +1,48 @@ import re +from collections.abc import Mapping, Sequence +from typing import Optional, TypedDict from talon import registry from ..conventions import get_cursorless_list_name -def get_list(name, type, descriptions=None): +class Variation(TypedDict): + spokenForm: str + description: str + + +class ListItemDescriptor(TypedDict): + id: str + type: str + variations: list[Variation] + + +def get_list( + name: str, type: str, descriptions: Optional[Mapping[str, str]] = None +) -> list[ListItemDescriptor]: if descriptions is None: descriptions = {} items = get_raw_list(name) - item_dict = items if isinstance(items, dict) else {item: item for item in items} - return make_dict_readable(type, item_dict, descriptions) + return make_dict_readable(type, items, descriptions) -def get_lists(names: list[str], type: str, descriptions=None): +def get_lists( + names: Sequence[str], type: str, descriptions: Optional[Mapping[str, str]] = None +) -> list[ListItemDescriptor]: return [item for name in names for item in get_list(name, type, descriptions)] -def get_raw_list(name): +def get_raw_list(name: str) -> Mapping[str, str]: cursorless_list_name = get_cursorless_list_name(name) return registry.lists[cursorless_list_name][0].copy() -def make_dict_readable(type: str, dict, descriptions=None): - if descriptions is None: - descriptions = {} - +def make_dict_readable( + type: str, dict: Mapping[str, str], descriptions: Mapping[str, str] +) -> list[ListItemDescriptor]: return [ { "id": value, @@ -43,7 +58,7 @@ def make_dict_readable(type: str, dict, descriptions=None): ] -def make_readable(text): +def make_readable(text: str) -> str: text = text.replace(".", " ") return de_camel(text).lower().capitalize() From adb0918338e1266b2a337b387bbfcee3b7924b63 Mon Sep 17 00:00:00 2001 From: David Vo Date: Sat, 16 Sep 2023 00:10:32 +1000 Subject: [PATCH 34/54] Tighten Talon marks type hints (#1848) This removes a bunch of `Any`s from the Talon code related to marks. Ref: #725 ## Checklist - [ ] ~I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/)~ - [ ] ~I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet)~ - [x] I have not broken the cheatsheet --- cursorless-talon/src/marks/decorated_mark.py | 11 +++---- cursorless-talon/src/marks/lines_number.py | 22 +++++++------- cursorless-talon/src/marks/mark.py | 6 ++-- cursorless-talon/src/marks/mark_types.py | 31 ++++++++++++++++++++ cursorless-talon/src/marks/simple_mark.py | 4 ++- cursorless-talon/src/targets/target_types.py | 8 +++-- 6 files changed, 58 insertions(+), 24 deletions(-) create mode 100644 cursorless-talon/src/marks/mark_types.py diff --git a/cursorless-talon/src/marks/decorated_mark.py b/cursorless-talon/src/marks/decorated_mark.py index 2a92f77c9a..75675ee895 100644 --- a/cursorless-talon/src/marks/decorated_mark.py +++ b/cursorless-talon/src/marks/decorated_mark.py @@ -4,6 +4,7 @@ from talon import Module, actions, cron, fs from ..csv_overrides import init_csv_and_watch_changes +from .mark_types import DecoratedSymbol mod = Module() @@ -28,9 +29,9 @@ def cursorless_grapheme(m) -> str: @mod.capture( rule="[{user.cursorless_hat_color}] [{user.cursorless_hat_shape}] " ) -def cursorless_decorated_symbol(m) -> dict[str, Any]: +def cursorless_decorated_symbol(m) -> DecoratedSymbol: """A decorated symbol""" - hat_color = getattr(m, "cursorless_hat_color", "default") + hat_color: str = getattr(m, "cursorless_hat_color", "default") try: hat_style_name = f"{hat_color}-{m.cursorless_hat_shape}" except AttributeError: @@ -82,10 +83,10 @@ def cursorless_decorated_symbol(m) -> dict[str, Any]: } FALLBACK_COLOR_ENABLEMENT = DEFAULT_COLOR_ENABLEMENT -unsubscribe_hat_styles = None +unsubscribe_hat_styles: Any = None -def setup_hat_styles_csv(hat_colors: dict, hat_shapes: dict): +def setup_hat_styles_csv(hat_colors: dict[str, str], hat_shapes: dict[str, str]): global unsubscribe_hat_styles ( @@ -149,7 +150,7 @@ def setup_hat_styles_csv(hat_colors: dict, hat_shapes: dict): slow_reload_job = None -def init_hats(hat_colors: dict, hat_shapes: dict): +def init_hats(hat_colors: dict[str, str], hat_shapes: dict[str, str]): setup_hat_styles_csv(hat_colors, hat_shapes) vscode_settings_path: Path = actions.user.vscode_settings_path().resolve() diff --git a/cursorless-talon/src/marks/lines_number.py b/cursorless-talon/src/marks/lines_number.py index 4f892cd12a..a1ff66fe62 100644 --- a/cursorless-talon/src/marks/lines_number.py +++ b/cursorless-talon/src/marks/lines_number.py @@ -1,10 +1,10 @@ from collections.abc import Callable from dataclasses import dataclass -from typing import Any from talon import Module from ..targets.range_target import RangeConnective +from .mark_types import LineNumber, LineNumberMark, LineNumberType mod = Module() @@ -14,8 +14,8 @@ @dataclass class CustomizableTerm: cursorlessIdentifier: str - type: str - formatter: Callable + type: LineNumberType + formatter: Callable[[int], int] # NOTE: Please do not change these dicts. Use the CSVs for customization. @@ -35,15 +35,13 @@ class CustomizableTerm: "[ ]" ) ) -def cursorless_line_number(m) -> dict[str, Any]: +def cursorless_line_number(m) -> LineNumber: direction = directions_map[m.cursorless_line_direction] - anchor = create_line_number_mark( - direction.type, direction.formatter(m.private_cursorless_number_small_list[0]) - ) - if len(m.private_cursorless_number_small_list) > 1: + numbers: list[int] = m.private_cursorless_number_small_list + anchor = create_line_number_mark(direction.type, direction.formatter(numbers[0])) + if len(numbers) > 1: active = create_line_number_mark( - direction.type, - direction.formatter(m.private_cursorless_number_small_list[1]), + direction.type, direction.formatter(numbers[1]) ) range_connective: RangeConnective = m.cursorless_range_connective return { @@ -56,9 +54,9 @@ def cursorless_line_number(m) -> dict[str, Any]: return anchor -def create_line_number_mark(line_number_type: str, line_number: int) -> dict[str, Any]: +def create_line_number_mark(type: LineNumberType, line_number: int) -> LineNumberMark: return { "type": "lineNumber", - "lineNumberType": line_number_type, + "lineNumberType": type, "lineNumber": line_number, } diff --git a/cursorless-talon/src/marks/mark.py b/cursorless-talon/src/marks/mark.py index 0231ebb936..18e21abb75 100644 --- a/cursorless-talon/src/marks/mark.py +++ b/cursorless-talon/src/marks/mark.py @@ -1,7 +1,7 @@ -from typing import Any - from talon import Module +from .mark_types import Mark + mod = Module() @@ -12,5 +12,5 @@ "" # row (ie absolute mod 100), up, down ) ) -def cursorless_mark(m) -> dict[str, Any]: +def cursorless_mark(m) -> Mark: return m[0] diff --git a/cursorless-talon/src/marks/mark_types.py b/cursorless-talon/src/marks/mark_types.py new file mode 100644 index 0000000000..3985b7d4f5 --- /dev/null +++ b/cursorless-talon/src/marks/mark_types.py @@ -0,0 +1,31 @@ +from typing import Literal, TypedDict, Union + + +class DecoratedSymbol(TypedDict): + type: Literal["decoratedSymbol"] + symbolColor: str + character: str + + +SimpleMark = dict[Literal["type"], str] + +LineNumberType = Literal["modulo100", "relative"] + + +class LineNumberMark(TypedDict): + type: Literal["lineNumber"] + lineNumberType: LineNumberType + lineNumber: int + + +class LineNumberRange(TypedDict): + type: Literal["range"] + anchor: LineNumberMark + active: LineNumberMark + excludeAnchor: bool + excludeActive: bool + + +LineNumber = Union[LineNumberMark, LineNumberRange] + +Mark = Union[DecoratedSymbol, SimpleMark, LineNumber] diff --git a/cursorless-talon/src/marks/simple_mark.py b/cursorless-talon/src/marks/simple_mark.py index 4359d05dba..a98d3d84c9 100644 --- a/cursorless-talon/src/marks/simple_mark.py +++ b/cursorless-talon/src/marks/simple_mark.py @@ -1,5 +1,7 @@ from talon import Module +from .mark_types import SimpleMark + mod = Module() mod.list("cursorless_simple_mark", desc="Cursorless simple marks") @@ -15,7 +17,7 @@ @mod.capture(rule="{user.cursorless_simple_mark}") -def cursorless_simple_mark(m) -> dict[str, str]: +def cursorless_simple_mark(m) -> SimpleMark: return { "type": simple_marks[m.cursorless_simple_mark], } diff --git a/cursorless-talon/src/targets/target_types.py b/cursorless-talon/src/targets/target_types.py index 6e130ce9f8..49e028322b 100644 --- a/cursorless-talon/src/targets/target_types.py +++ b/cursorless-talon/src/targets/target_types.py @@ -1,5 +1,7 @@ from dataclasses import dataclass -from typing import Literal, Optional, Union +from typing import Any, Literal, Optional, Union + +from ..marks.mark_types import Mark RangeTargetType = Literal["vertical"] @@ -7,8 +9,8 @@ @dataclass class PrimitiveTarget: type = "primitive" - mark: Optional[dict] - modifiers: Optional[list[dict]] + mark: Optional[Mark] + modifiers: Optional[list[dict[str, Any]]] @dataclass From 3b38e7b89679091ff0cee4b32eee71a821807d5a Mon Sep 17 00:00:00 2001 From: David Vo Date: Sat, 16 Sep 2023 13:18:31 +1000 Subject: [PATCH 35/54] Tighten and fix Talon actions type hints (#1849) This fixes an incorrect type hint, removes a few implicit `Any`s, and tightens up how `cursorless_get_text_action` can be used. ## Checklist - [ ] ~I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/)~ - [ ] ~I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet)~ - [x] I have not broken the cheatsheet Co-authored-by: Pokey Rule <755842+pokey@users.noreply.github.com> --- cursorless-talon/src/actions/actions.py | 9 ++++----- cursorless-talon/src/actions/get_text.py | 3 ++- cursorless-talon/src/actions/homophones.py | 12 +++++++----- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/cursorless-talon/src/actions/actions.py b/cursorless-talon/src/actions/actions.py index 0fb6ef4dbb..55a8bd7763 100644 --- a/cursorless-talon/src/actions/actions.py +++ b/cursorless-talon/src/actions/actions.py @@ -1,4 +1,4 @@ -from typing import Union +from typing import Callable, Union from talon import Module, actions @@ -15,7 +15,6 @@ mod = Module() - mod.list( "cursorless_simple_action", desc="Cursorless internal: simple actions", @@ -48,7 +47,7 @@ "experimental_action", ] -callback_actions = { +callback_actions: dict[str, Callable[[CursorlessTarget], None]] = { "callAsFunction": cursorless_call_action, "findInDocument": actions.user.private_cursorless_find, "nextHomophone": cursorless_homophones_action, @@ -74,7 +73,7 @@ "{user.cursorless_custom_action}" ) ) -def cursorless_action_or_ide_command(m) -> dict: +def cursorless_action_or_ide_command(m) -> dict[str, str]: try: value = m.cursorless_custom_action type = "ide_command" @@ -127,7 +126,7 @@ def cursorless_insert( cursorless_replace_action(destination, text) def private_cursorless_action_or_ide_command( - instruction: dict, target: CursorlessTarget + instruction: dict[str, str], target: CursorlessTarget ): """Perform cursorless action or ide command on target (internal use only)""" type = instruction["type"] diff --git a/cursorless-talon/src/actions/get_text.py b/cursorless-talon/src/actions/get_text.py index 1b38f49a1a..8ee8849da8 100644 --- a/cursorless-talon/src/actions/get_text.py +++ b/cursorless-talon/src/actions/get_text.py @@ -7,11 +7,12 @@ def cursorless_get_text_action( target: CursorlessTarget, + *, show_decorations: Optional[bool] = None, ensure_single_target: Optional[bool] = None, ) -> list[str]: """Get target texts""" - options = {} + options: dict[str, bool] = {} if show_decorations is not None: options["showDecorations"] = show_decorations diff --git a/cursorless-talon/src/actions/homophones.py b/cursorless-talon/src/actions/homophones.py index 3b7a7408d7..df571f9469 100644 --- a/cursorless-talon/src/actions/homophones.py +++ b/cursorless-talon/src/actions/homophones.py @@ -1,11 +1,13 @@ +from typing import Optional + from talon import actions, app -from ..targets.target_types import PrimitiveDestination +from ..targets.target_types import CursorlessTarget, PrimitiveDestination from .get_text import cursorless_get_text_action from .replace import cursorless_replace_action -def cursorless_homophones_action(target: dict): +def cursorless_homophones_action(target: CursorlessTarget): """Replaced target with next homophone""" texts = cursorless_get_text_action(target, show_decorations=False) try: @@ -17,8 +19,8 @@ def cursorless_homophones_action(target: dict): cursorless_replace_action(destination, updated_texts) -def get_next_homophone(word: str): - homophones = actions.user.homophones_get(word) +def get_next_homophone(word: str) -> str: + homophones: Optional[list[str]] = actions.user.homophones_get(word) if not homophones: raise LookupError(f"Found no homophones for '{word}'") index = (homophones.index(word.lower()) + 1) % len(homophones) @@ -26,7 +28,7 @@ def get_next_homophone(word: str): return format_homophone(word, homophone) -def format_homophone(word: str, homophone: str): +def format_homophone(word: str, homophone: str) -> str: if word.isupper(): return homophone.upper() if word == word.capitalize(): From 3eb7f1b8145cae8843f7cf6b8530d3b0dc7f71af Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Wed, 20 Sep 2023 12:11:22 +0200 Subject: [PATCH 36/54] Made python match a cursorless statement scope (#1907) ## Checklist - [x] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [/] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [/] I have not broken the cheatsheet --- .../recorded/languages/python/changeState.yml | 26 +++++++++++++++++++ queries/python.scm | 1 + 2 files changed, 27 insertions(+) create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeState.yml diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeState.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeState.yml new file mode 100644 index 0000000000..37b9aff44f --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeState.yml @@ -0,0 +1,26 @@ +languageId: python +command: + version: 6 + spokenForm: change state + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: statement} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + match value: + case 0: + pass + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: "" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/queries/python.scm b/queries/python.scm index 094f916174..eb5e62aeb7 100644 --- a/queries/python.scm +++ b/queries/python.scm @@ -17,6 +17,7 @@ (if_statement) (import_from_statement) (import_statement) + (match_statement) (nonlocal_statement) (pass_statement) (print_statement) From 7369ceaa0dadb877549e328b518ad29f996e4513 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 21 Sep 2023 12:26:25 +0100 Subject: [PATCH 37/54] Fix "from" section in docs (#1906) It got orphaned from its parent in a recent commit ## Checklist - [ ] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [ ] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [ ] I have not broken the cheatsheet --- docs/user/README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/user/README.md b/docs/user/README.md index 15850b3aca..152b7cd1ec 100644 --- a/docs/user/README.md +++ b/docs/user/README.md @@ -351,6 +351,15 @@ If your cursor is touching a token, you can say `"take every instance"` to selec Pro tip: if you say eg `"take five instances air"`, and it turns out you need more, you can say eg `"take that and next two instances that"` to select the next two instances after the last instance you selected. +###### Experimental: `"from"` + +We have experimental support for prefixing a command with `"from "` to narrow the range within which `"every instance"` searches, or to set the start point from which `"next instance"` searches. For example: + +- `"from funk take every instance air"`: selects all instances of the token with a hat over the letter `a` in the current function +- `"from air take next instance bat"`: selects the next instance of the token with a hat over the letter `b` starting from the token with a hat over the letter `a` + +Note that the `"from"` modifier is not enabled by default; you must remove the `-` at the start of the line starting with `-from` in your `experimental/experimental_actions.csv` [settings csv](./customization.md). Note also that this feature is considered experimental and may change in the future. + ##### `"just"` The `"just"` modifier strips the target of any semantic information, treating it as just a raw range, with the following effects: @@ -378,15 +387,6 @@ Some examples: - `"chuck just line"`: deletes just the content of the line, leaving a blank line. - `"bring bat after just air"`: results in something like `aaabbb` where the bat token was copied after the air token with no delimeter between them. -###### Experimental: `"from"` - -We have experimental support for prefixing a command with `"from "` to narrow the range within which `"every instance"` searches, or to set the start point from which `"next instance"` searches. For example: - -- `"from funk take every instance air"`: selects all instances of the token with a hat over the letter `a` in the current function -- `"from air take next instance bat"`: selects the next instance of the token with a hat over the letter `b` starting from the token with a hat over the letter `a` - -Note that the `"from"` modifier is not enabled by default; you must remove the `-` at the start of the line starting with `-from` in your `experimental/experimental_actions.csv` [settings csv](./customization.md). Note also that this feature is considered experimental and may change in the future. - ##### Surrounding pair Cursorless has support for expanding the selection to the nearest containing paired delimiter, eg the surrounding parentheses, curly brackets, etc. From 88e858e08ed74f25969536884c5629aaf28ea72e Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 27 Sep 2023 06:29:48 +0100 Subject: [PATCH 38/54] Add Andreas to codeowners (#1913) ## Checklist - [ ] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [ ] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [ ] I have not broken the cheatsheet --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 33f266adbb..79ab9f5f65 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @pokey +* @pokey @AndreasArvidsson From 9ab56fdd661ec5ccf7787d9932187349eeae4dc8 Mon Sep 17 00:00:00 2001 From: David Vo Date: Wed, 27 Sep 2023 22:32:49 +1000 Subject: [PATCH 39/54] Fix Black (#1910) Fixes #1909 --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: Pokey Rule <755842+pokey@users.noreply.github.com> --- pyproject.toml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b1327e6966..31dab584d0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,11 @@ [tool.black] target-version = ["py39"] -force-exclude = ["vendor", "data/playground"] +force-exclude = ''' +( + ^/vendor/ + | ^/data/playground/ +) +''' [tool.ruff] select = ["E", "F", "C4", "I001", "UP", "SIM"] From 11f9de79d26f9bc8464878b76d3fccf4301045ea Mon Sep 17 00:00:00 2001 From: Aaron Adams Date: Thu, 28 Sep 2023 17:53:16 +0800 Subject: [PATCH 40/54] Fix delimiters typo (#1915) ## Checklist - [-] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [x] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [-] I have not broken the cheatsheet Co-authored-by: fidgetingbits --- docs/user/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user/README.md b/docs/user/README.md index 152b7cd1ec..2c3906a233 100644 --- a/docs/user/README.md +++ b/docs/user/README.md @@ -376,7 +376,7 @@ The `"just"` modifier strips the target of any semantic information, treating it - `"chuck just line"` will delete only the content of the current line, without removing the line ending, resulting in a blank line, unlike the default behaviour of `"chuck line"` that removes the line entirely, leaving no blank line. -- A raw range does not have its own insertion delimitiers. +- A raw range does not have its own insertion delimiters. - For example, `"paste after just air"` will paste directly after the air token, without inserting a space, as opposed to the way `"paste after air"` would insert a space before the pasted content. - If you use `"just"` on the destination of a `"bring"` command, it will inherit its insertion delimiters from the source of the `"bring"` action. For example, in the command `"bring arg air and bat after just paren"`, the `"air"` and `"bat"` arguments will be joined by commas. In contrast, `"bring arg air and bat after token paren"` would join the arguments with spaces. - In the case of [`"instance"`](#instance), by default `"every instance air"` will only consider instances of the air token that are themselves full tokens, but `"every instance just air"` doesn't have such a restriction, because we've stripped air of its semantic "token-ness". From be61609bbe7c3b4942114773a721c7fb62e570b0 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 28 Sep 2023 12:17:32 +0200 Subject: [PATCH 41/54] Added playground action (#1894) Fixes #1893 `"parse tree class"` ![image](https://github.com/cursorless-dev/cursorless/assets/3511326/d5dfcae1-10a2-46bf-8f42-f75a680ddd63) ## Checklist - [x] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [/] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [/] I have not broken the cheatsheet --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- cursorless-talon-dev/src/cursorless_dev.talon | 5 + packages/common/src/ide/PassthroughIDEBase.ts | 14 +- packages/common/src/ide/fake/FakeIDE.ts | 13 +- packages/common/src/ide/types/ide.types.ts | 15 +++ .../src/types/command/ActionDescriptor.ts | 1 + .../cursorless-engine/src/actions/Actions.ts | 4 + .../src/actions/ShowParseTree.ts | 121 ++++++++++++++++++ .../cursorless-engine/src/cursorlessEngine.ts | 2 + .../defaultSpokenForms/actions.ts | 1 + packages/cursorless-engine/src/runCommand.ts | 7 +- .../src/test/fixtures/talonApi.fixture.ts | 5 + .../recorded/actions/parseTreeFile.yml | 30 +++++ .../src/ide/vscode/VscodeIDE.ts | 12 +- 13 files changed, 222 insertions(+), 8 deletions(-) create mode 100644 packages/cursorless-engine/src/actions/ShowParseTree.ts create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/parseTreeFile.yml diff --git a/cursorless-talon-dev/src/cursorless_dev.talon b/cursorless-talon-dev/src/cursorless_dev.talon index 54582af86d..e777549d88 100644 --- a/cursorless-talon-dev/src/cursorless_dev.talon +++ b/cursorless-talon-dev/src/cursorless_dev.talon @@ -1,3 +1,5 @@ +mode: command +mode: user.cursorless_spoken_form_test tag: user.cursorless - @@ -28,3 +30,6 @@ tag: user.cursorless test snippet make : user.private_cursorless_make_snippet_test(cursorless_target) + +parse tree : + user.cursorless_command("private.showParseTree", cursorless_target) diff --git a/packages/common/src/ide/PassthroughIDEBase.ts b/packages/common/src/ide/PassthroughIDEBase.ts index 23dc1e034a..44d57f4128 100644 --- a/packages/common/src/ide/PassthroughIDEBase.ts +++ b/packages/common/src/ide/PassthroughIDEBase.ts @@ -10,7 +10,13 @@ import { TextEditorVisibleRangesChangeEvent, } from "./types/events.types"; import { FlashDescriptor } from "./types/FlashDescriptor"; -import { Disposable, IDE, RunMode, WorkspaceFolder } from "./types/ide.types"; +import { + Disposable, + IDE, + OpenUntitledTextDocumentOptions, + RunMode, + WorkspaceFolder, +} from "./types/ide.types"; import { Messages } from "./types/Messages"; import { QuickPickOptions } from "./types/QuickPickOptions"; import { State } from "./types/State"; @@ -137,6 +143,12 @@ export default class PassthroughIDEBase implements IDE { return this.original.openTextDocument(path); } + public openUntitledTextDocument( + options?: OpenUntitledTextDocumentOptions, + ): Promise { + return this.original.openUntitledTextDocument(options); + } + public showQuickPick( items: readonly string[], options?: QuickPickOptions, diff --git a/packages/common/src/ide/fake/FakeIDE.ts b/packages/common/src/ide/fake/FakeIDE.ts index 7bb1069fbe..4dddee295b 100644 --- a/packages/common/src/ide/fake/FakeIDE.ts +++ b/packages/common/src/ide/fake/FakeIDE.ts @@ -1,21 +1,22 @@ -import type { EditableTextEditor, TextEditor } from "../.."; import { pull } from "lodash"; +import type { EditableTextEditor, TextEditor } from "../.."; import { GeneralizedRange } from "../../types/GeneralizedRange"; import { TextDocument } from "../../types/TextDocument"; import type { TextDocumentChangeEvent } from "../types/Events"; +import { FlashDescriptor } from "../types/FlashDescriptor"; +import { QuickPickOptions } from "../types/QuickPickOptions"; import { Event, TextEditorSelectionChangeEvent, TextEditorVisibleRangesChangeEvent, } from "../types/events.types"; -import { FlashDescriptor } from "../types/FlashDescriptor"; import type { Disposable, IDE, + OpenUntitledTextDocumentOptions, RunMode, WorkspaceFolder, } from "../types/ide.types"; -import { QuickPickOptions } from "../types/QuickPickOptions"; import { FakeCapabilities } from "./FakeCapabilities"; import FakeClipboard from "./FakeClipboard"; import FakeConfiguration from "./FakeConfiguration"; @@ -92,6 +93,12 @@ export default class FakeIDE implements IDE { throw Error("Not implemented"); } + public openUntitledTextDocument( + _options: OpenUntitledTextDocumentOptions, + ): Promise { + throw Error("Not implemented"); + } + public setQuickPickReturnValue(value: string | undefined) { this.quickPickReturnValue = value; } diff --git a/packages/common/src/ide/types/ide.types.ts b/packages/common/src/ide/types/ide.types.ts index c99e1e711d..3cb67325e8 100644 --- a/packages/common/src/ide/types/ide.types.ts +++ b/packages/common/src/ide/types/ide.types.ts @@ -22,6 +22,10 @@ import { State } from "./State"; export type RunMode = "production" | "development" | "test"; export type HighlightId = string; +export interface OpenUntitledTextDocumentOptions { + language?: string; + content?: string; +} export interface IDE { readonly configuration: Configuration; @@ -105,6 +109,17 @@ export interface IDE { */ openTextDocument(path: string): Promise; + /** + * Opens an untitled document. + * + * @see {@link openTextDocument} + * @param options optional language and documents content + * @return An editor + */ + openUntitledTextDocument( + options?: OpenUntitledTextDocumentOptions, + ): Promise; + /** * An event that is emitted when a {@link TextDocument text document} is opened or when the language id * of a text document {@link languages.setTextDocumentLanguage has been changed}. diff --git a/packages/common/src/types/command/ActionDescriptor.ts b/packages/common/src/types/command/ActionDescriptor.ts index b465d81e67..a0c43cf73a 100644 --- a/packages/common/src/types/command/ActionDescriptor.ts +++ b/packages/common/src/types/command/ActionDescriptor.ts @@ -46,6 +46,7 @@ const simpleActionNames = [ "toggleLineBreakpoint", "toggleLineComment", "unfoldRegion", + "private.showParseTree", "private.getTargets", ] as const; diff --git a/packages/cursorless-engine/src/actions/Actions.ts b/packages/cursorless-engine/src/actions/Actions.ts index 5b70327b0e..c338a82d6e 100644 --- a/packages/cursorless-engine/src/actions/Actions.ts +++ b/packages/cursorless-engine/src/actions/Actions.ts @@ -1,3 +1,4 @@ +import { TreeSitter } from ".."; import { Snippets } from "../core/Snippets"; import { RangeUpdater } from "../core/updateSelections/RangeUpdater"; import { ModifierStageFactory } from "../processTargets/ModifierStageFactory"; @@ -26,6 +27,7 @@ import { } from "./InsertEmptyLines"; import InsertSnippet from "./InsertSnippet"; import { PasteFromClipboard } from "./PasteFromClipboard"; +import ShowParseTree from "./ShowParseTree"; import Remove from "./Remove"; import Replace from "./Replace"; import Rewrap from "./Rewrap"; @@ -63,6 +65,7 @@ import { ActionRecord } from "./actions.types"; */ export class Actions implements ActionRecord { constructor( + private treeSitter: TreeSitter, private snippets: Snippets, private rangeUpdater: RangeUpdater, private modifierStageFactory: ModifierStageFactory, @@ -145,5 +148,6 @@ export class Actions implements ActionRecord { this.snippets, this.modifierStageFactory, ); + ["private.showParseTree"] = new ShowParseTree(this.treeSitter); ["private.getTargets"] = new GetTargets(); } diff --git a/packages/cursorless-engine/src/actions/ShowParseTree.ts b/packages/cursorless-engine/src/actions/ShowParseTree.ts new file mode 100644 index 0000000000..0c7db86232 --- /dev/null +++ b/packages/cursorless-engine/src/actions/ShowParseTree.ts @@ -0,0 +1,121 @@ +import { FlashStyle, Range, TextDocument } from "@cursorless/common"; +import * as path from "node:path"; +import type { Tree, TreeCursor } from "web-tree-sitter"; +import type { TreeSitter } from ".."; +import { ide } from "../singletons/ide.singleton"; +import type { Target } from "../typings/target.types"; +import { flashTargets } from "../util/targetUtils"; +import type { ActionReturnValue } from "./actions.types"; + +export default class ShowParseTree { + constructor(private treeSitter: TreeSitter) { + this.run = this.run.bind(this); + } + + async run(targets: Target[]): Promise { + await flashTargets(ide(), targets, FlashStyle.referenced); + + const results: string[] = ["# Cursorless parse tree"]; + + for (const target of targets) { + const { editor, contentRange } = target; + const tree = this.treeSitter.getTree(editor.document); + results.push(parseTree(editor.document, tree, contentRange)); + } + + ide().openUntitledTextDocument({ + language: "markdown", + content: results.join("\n\n"), + }); + + return { thatTargets: targets }; + } +} + +function parseTree( + document: TextDocument, + tree: Tree, + contentRange: Range, +): string { + const resultPlayground: string[] = []; + const resultQuery: string[] = []; + + parseCursor(resultPlayground, resultQuery, contentRange, tree.walk(), 0); + + return [ + `## ${path.basename(document.uri.path)} [${contentRange}]\n`, + `\`\`\`${document.languageId}`, + document.getText(contentRange), + "```", + "", + "```scm", + ...resultQuery, + "```", + "", + "```js", + ...resultPlayground, + "```", + "", + ].join("\n"); +} + +function parseCursor( + resultPlayground: string[], + resultQuery: string[], + contentRange: Range, + cursor: TreeCursor, + numIndents: number, +): void { + while (true) { + const nodeRange = new Range( + cursor.startPosition.row, + cursor.startPosition.column, + cursor.endPosition.row, + cursor.endPosition.column, + ); + + if (contentRange.intersection(nodeRange) != null) { + const indentation = " ".repeat(numIndents); + const fieldName = getFieldName(cursor); + const prefix = indentation + fieldName; + + // Named node + if (cursor.nodeIsNamed) { + resultPlayground.push(`${prefix}${cursor.nodeType} [${nodeRange}]`); + resultQuery.push(`${prefix}(${cursor.nodeType}`); + + // Named node with children + if (cursor.gotoFirstChild()) { + parseCursor( + resultPlayground, + resultQuery, + contentRange, + cursor, + numIndents + 1, + ); + cursor.gotoParent(); + resultQuery.push(`${indentation})`); + } + // Named node without children + else { + resultQuery[resultQuery.length - 1] += ")"; + } + } + // Anonymous node + else { + const type = `"${cursor.nodeType}"`; + resultPlayground.push(`${prefix}${type} [${nodeRange}]`); + resultQuery.push(`${prefix}${type}`); + } + } + + if (!cursor.gotoNextSibling()) { + return; + } + } +} + +function getFieldName(cursor: TreeCursor): string { + const field = cursor.currentFieldName(); + return field != null ? `${field}: ` : ""; +} diff --git a/packages/cursorless-engine/src/cursorlessEngine.ts b/packages/cursorless-engine/src/cursorlessEngine.ts index ab0c96b495..b45fbc993e 100644 --- a/packages/cursorless-engine/src/cursorlessEngine.ts +++ b/packages/cursorless-engine/src/cursorlessEngine.ts @@ -59,6 +59,7 @@ export function createCursorlessEngine( commandApi: { runCommand(command: Command) { return runCommand( + treeSitter, debug, hatTokenMap, testCaseRecorder, @@ -72,6 +73,7 @@ export function createCursorlessEngine( runCommandSafe(...args: unknown[]) { return runCommand( + treeSitter, debug, hatTokenMap, testCaseRecorder, diff --git a/packages/cursorless-engine/src/generateSpokenForm/defaultSpokenForms/actions.ts b/packages/cursorless-engine/src/generateSpokenForm/defaultSpokenForms/actions.ts index e1009625d0..c15e47d38f 100644 --- a/packages/cursorless-engine/src/generateSpokenForm/defaultSpokenForms/actions.ts +++ b/packages/cursorless-engine/src/generateSpokenForm/defaultSpokenForms/actions.ts @@ -54,6 +54,7 @@ export const actions = { insertSnippet: "snippet", pasteFromClipboard: "paste", + ["private.showParseTree"]: "parse tree", ["experimental.setInstanceReference"]: "from", editNew: null, diff --git a/packages/cursorless-engine/src/runCommand.ts b/packages/cursorless-engine/src/runCommand.ts index d6d0a02a14..619d71350d 100644 --- a/packages/cursorless-engine/src/runCommand.ts +++ b/packages/cursorless-engine/src/runCommand.ts @@ -6,7 +6,7 @@ import { Snippets } from "./core/Snippets"; import { CommandRunnerImpl } from "./core/commandRunner/CommandRunnerImpl"; import { canonicalizeAndValidateCommand } from "./core/commandVersionUpgrades/canonicalizeAndValidateCommand"; import { RangeUpdater } from "./core/updateSelections/RangeUpdater"; -import { StoredTargetMap, TestCaseRecorder } from "./index"; +import { StoredTargetMap, TestCaseRecorder, TreeSitter } from "./index"; import { LanguageDefinitions } from "./languages/LanguageDefinitions"; import { TargetPipelineRunner } from "./processTargets"; import { MarkStageFactoryImpl } from "./processTargets/MarkStageFactoryImpl"; @@ -26,6 +26,7 @@ import { ScopeHandlerFactoryImpl } from "./processTargets/modifiers/scopeHandler * 5. Call {@link CommandRunnerImpl.run} to run the actual command. */ export async function runCommand( + treeSitter: TreeSitter, debug: Debug, hatTokenMap: HatTokenMap, testCaseRecorder: TestCaseRecorder, @@ -47,6 +48,7 @@ export async function runCommand( ); let commandRunner = createCommandRunner( + treeSitter, languageDefinitions, debug, storedTargets, @@ -66,6 +68,7 @@ export async function runCommand( } function createCommandRunner( + treeSitter: TreeSitter, languageDefinitions: LanguageDefinitions, debug: Debug, storedTargets: StoredTargetMap, @@ -92,6 +95,6 @@ function createCommandRunner( debug, storedTargets, targetPipelineRunner, - new Actions(snippets, rangeUpdater, modifierStageFactory), + new Actions(treeSitter, snippets, rangeUpdater, modifierStageFactory), ); } diff --git a/packages/cursorless-engine/src/test/fixtures/talonApi.fixture.ts b/packages/cursorless-engine/src/test/fixtures/talonApi.fixture.ts index 1ca8b892fd..959a5b2f84 100644 --- a/packages/cursorless-engine/src/test/fixtures/talonApi.fixture.ts +++ b/packages/cursorless-engine/src/test/fixtures/talonApi.fixture.ts @@ -79,6 +79,10 @@ const wrapWithSnippetByNameAction: ActionDescriptor = { variableName: "body", }, }; +const parseTreeAction: ActionDescriptor = { + name: "private.showParseTree", + target: decoratedPrimitiveTarget("a"), +}; /** * These test our Talon api using dummy spoken forms defined in @@ -99,6 +103,7 @@ export const talonApiFixture = [ "test api wrap with snippet by name this", wrapWithSnippetByNameAction, ), + spokenFormTest("parse tree air", parseTreeAction), ]; function decoratedPrimitiveTarget( diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/parseTreeFile.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/parseTreeFile.yml new file mode 100644 index 0000000000..95de60ec68 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/parseTreeFile.yml @@ -0,0 +1,30 @@ +languageId: typescript +command: + version: 6 + spokenForm: parse tree file + action: + name: private.showParseTree + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: document} + usePrePhraseSnapshot: true +initialState: + documentContents: const value = 2; + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: const value = 2; + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + thatMark: + - type: DocumentTarget + contentRange: + start: {line: 0, character: 0} + end: {line: 0, character: 16} + isReversed: false + hasExplicitRange: true diff --git a/packages/cursorless-vscode/src/ide/vscode/VscodeIDE.ts b/packages/cursorless-vscode/src/ide/vscode/VscodeIDE.ts index 761bfb5fd9..5993911c96 100644 --- a/packages/cursorless-vscode/src/ide/vscode/VscodeIDE.ts +++ b/packages/cursorless-vscode/src/ide/vscode/VscodeIDE.ts @@ -6,6 +6,7 @@ import { HighlightId, IDE, InputBoxOptions, + OpenUntitledTextDocumentOptions, OutdatedExtensionError, QuickPickOptions, RunMode, @@ -19,7 +20,7 @@ import { import { pull } from "lodash"; import { v4 as uuid } from "uuid"; import * as vscode from "vscode"; -import { ExtensionContext, window, workspace, WorkspaceFolder } from "vscode"; +import { ExtensionContext, WorkspaceFolder, window, workspace } from "vscode"; import { VscodeCapabilities } from "./VscodeCapabilities"; import VscodeClipboard from "./VscodeClipboard"; import VscodeConfiguration from "./VscodeConfiguration"; @@ -29,9 +30,9 @@ import VscodeGlobalState from "./VscodeGlobalState"; import VscodeHighlights, { HighlightStyle } from "./VscodeHighlights"; import VscodeMessages from "./VscodeMessages"; import { vscodeRunMode } from "./VscodeRunMode"; -import { vscodeShowQuickPick } from "./vscodeShowQuickPick"; import { VscodeTextDocumentImpl } from "./VscodeTextDocumentImpl"; import { VscodeTextEditorImpl } from "./VscodeTextEditorImpl"; +import { vscodeShowQuickPick } from "./vscodeShowQuickPick"; export class VscodeIDE implements IDE { readonly configuration: VscodeConfiguration; @@ -126,6 +127,13 @@ export class VscodeIDE implements IDE { return this.fromVscodeEditor(await window.showTextDocument(textDocument)); } + public async openUntitledTextDocument( + options?: OpenUntitledTextDocumentOptions, + ): Promise { + const textDocument = await workspace.openTextDocument(options); + return this.fromVscodeEditor(await window.showTextDocument(textDocument)); + } + public async showInputBox( options?: InputBoxOptions, ): Promise { From 128933aec052cb4ac957d4071b0b976be0c34b64 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 28 Sep 2023 11:31:22 +0100 Subject: [PATCH 42/54] knausj => community (#1914) Also tweaked unicode docs slightly while I was there ## Checklist - [ ] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [ ] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [ ] I have not broken the cheatsheet --- CHANGELOG.md | 2 +- docs/contributing/architecture/hat-snapshots.md | 2 +- docs/user/experimental/snippets.md | 2 +- docs/user/installation.md | 16 ++++++++-------- docs/user/unicode.md | 4 ++-- packages/common/src/cursorlessCommandIds.ts | 2 +- .../tokenGraphemeSplitter.ts | 2 +- packages/cursorless-vscode/package.json | 2 +- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c47102693f..8a1817e535 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -139,7 +139,7 @@ There's too much new stuff in this release to fit in a highlights reel, so we'll - Improve and unify selection updating behaviour to handle overlapping ranges [\#138](https://github.com/cursorless-dev/cursorless/issues/138) - Duplicate symbols after VS Code update [\#111](https://github.com/cursorless-dev/cursorless/issues/111) - Fold action not working properly with multiple list elements [\#39](https://github.com/cursorless-dev/cursorless/issues/39) -- The `clear` command clashes with Knausj commands [\#68](https://github.com/pokey/cursorless-talon/issues/68) +- The `clear` command clashes with community commands [\#68](https://github.com/pokey/cursorless-talon/issues/68) **Closed issues:** diff --git a/docs/contributing/architecture/hat-snapshots.md b/docs/contributing/architecture/hat-snapshots.md index 1ead256699..de4c33c4f3 100644 --- a/docs/contributing/architecture/hat-snapshots.md +++ b/docs/contributing/architecture/hat-snapshots.md @@ -2,7 +2,7 @@ In order to allow long chained command phrases, we take a snapshot of the hat token map at the start of a phrase and continue to use this map during the course of the entire phrase. This way you can be sure that any commands issued during the course of a single phrase that refer to a decorated token will continue to refer to the same logical token no matter what happens in the document during phrase execution. Note that the ranges of tokens will be kept current as the document changes so that they refer to the same logical range, but the same logical token will keep the same key in the hat token map over the course of a phrase. -To make this work, first the voice engine [touches](https://github.com/knausj85/knausj_talon/blob/e373780af16256ab8fd5638af32d97fa23c4c0fc/apps/vscode/command_client/command_client.py#L398) a file within the signals subdirectory of the command server communication directory after the phrase has been parsed but right before execution begins. Then cursorless will check the version of the signal file before it [reads](https://github.com/cursorless-dev/cursorless/blob/2a624888369d41b0531e472d001d63d09912c8aa/src/core/HatTokenMap.ts#L88) or [updates](https://github.com/cursorless-dev/cursorless/blob/0d1004bafc6764734bee62afbfbb02500630a264/src/core/HatTokenMap.ts#L70) the hat token map via the command server [signal API](https://github.com/pokey/command-server/blob/2b9f9ea2a38b6e95aa60ff9553a804165e527308/src/extension.ts#L29). If the signal has been emitted since the last time cursorless took a snapshot of the hat token map, it will take a new snapshot and continue to use that snapshot of the hats until the next time the signal is emitted. Note that the signal transmission is asynchronous so cursorless just needs to make sure to check the version of the signal before it either updates or reads the map. +To make this work, first the voice engine [touches](https://github.com/talonhub/community/blob/e373780af16256ab8fd5638af32d97fa23c4c0fc/apps/vscode/command_client/command_client.py#L398) a file within the signals subdirectory of the command server communication directory after the phrase has been parsed but right before execution begins. Then cursorless will check the version of the signal file before it [reads](https://github.com/cursorless-dev/cursorless/blob/2a624888369d41b0531e472d001d63d09912c8aa/src/core/HatTokenMap.ts#L88) or [updates](https://github.com/cursorless-dev/cursorless/blob/0d1004bafc6764734bee62afbfbb02500630a264/src/core/HatTokenMap.ts#L70) the hat token map via the command server [signal API](https://github.com/pokey/command-server/blob/2b9f9ea2a38b6e95aa60ff9553a804165e527308/src/extension.ts#L29). If the signal has been emitted since the last time cursorless took a snapshot of the hat token map, it will take a new snapshot and continue to use that snapshot of the hats until the next time the signal is emitted. Note that the signal transmission is asynchronous so cursorless just needs to make sure to check the version of the signal before it either updates or reads the map. In the diagram below, we document the flow of a couple different cases. Dotted lines represent asynchronous / fire-and-forget communication: diff --git a/docs/user/experimental/snippets.md b/docs/user/experimental/snippets.md index 86f7c8535d..a53aa03f80 100644 --- a/docs/user/experimental/snippets.md +++ b/docs/user/experimental/snippets.md @@ -92,7 +92,7 @@ Note that each snippet can use `insertionScopeTypes` to indicate that it will au As usual, the spoken forms for these snippets can be [customized by csv](../customization.md). The csvs are in the files in `cursorless-settings/experimental` with `snippet` in their name. -In addition, you can change the term `"snippet"` (for snippet insertion) using actions.csv. Keep in mind that if you change it to `"snip"`, you may want to turn off the built-in knausj `"snip"` commands to avoid conflicts. +In addition, you can change the term `"snippet"` (for snippet insertion) using actions.csv. Keep in mind that if you change it to `"snip"`, you may want to turn off the built-in community `"snip"` commands to avoid conflicts. ## Adding your own snippets diff --git a/docs/user/installation.md b/docs/user/installation.md index 88ce69c42c..ce9adcbeb2 100644 --- a/docs/user/installation.md +++ b/docs/user/installation.md @@ -1,8 +1,8 @@ # Installation 1. Install [Talon](https://talonvoice.com/) -2. Install [knausj_talon](https://github.com/knausj85/knausj_talon). - _(Or see [here](https://github.com/cursorless-dev/cursorless/wiki/Talon-home-requirements) if you prefer not to use knausj.)_ +2. Install the [community Talon commands](https://github.com/talonhub/community). + _(Or see [here](https://github.com/cursorless-dev/cursorless/wiki/Talon-home-requirements) if you prefer not to use community.)_ 3. Install [VSCode](https://code.visualstudio.com/) 4. Install the [VSCode talon extension pack](https://marketplace.visualstudio.com/items?itemName=pokey.talon) 5. Install the [Cursorless VSCode extension](https://marketplace.visualstudio.com/items?itemName=pokey.cursorless) @@ -25,9 +25,9 @@ Alternatively, access the directory by right clicking the Talon icon in taskbar, The folder structure should look something like the below: ```insert code: -~/.talon/user/knausj_talon -~/.talon/user/knausj_talon/apps -~/.talon/user/knausj_talon/code +~/.talon/user/community +~/.talon/user/community/apps +~/.talon/user/community/code ... ~/.talon/user/cursorless-talon ~/.talon/user/cursorless-talon/src @@ -50,9 +50,9 @@ Alternatively, access the directory by right clicking the Talon icon in taskbar, The folder structure should look something like the below: ```insert code: -%AppData%\Talon\user\knausj_talon -%AppData%\Talon\user\knausj_talon\apps -%AppData%\Talon\user\knausj_talon\code +%AppData%\Talon\user\community +%AppData%\Talon\user\community\apps +%AppData%\Talon\user\community\code ... %AppData%\Talon\user\cursorless-talon %AppData%\Talon\user\cursorless-talon\src diff --git a/docs/user/unicode.md b/docs/user/unicode.md index 16195191a1..a8680e88f4 100644 --- a/docs/user/unicode.md +++ b/docs/user/unicode.md @@ -7,11 +7,11 @@ Cursorless has first-class support for Unicode. By default, when constructing ha - Africa - África -For Unicode symbols that are not letters, and that are not speakable in knausj, for example emoji, Chinese characters, etc, we have a special "character" called `"special"` that can be used. So for example, if there were a blue hat over a '😄' character, you could say `"take blue special"` to select it. As always, the spoken form `"special"` can be [customized](customization.md). +For Unicode symbols that are not letters, and that are not speakable by default, for example emoji, Chinese characters, etc, we have a special "character" called `"special"` that can be used. So for example, if there were a blue hat over a '😄' character, you could say `"take blue special"` to select it. As always, the spoken form `"special"` can be [customized](customization.md). ## Advanced customization -The above setup will allow you to refer to any Unicode token. However, if you have overridden your `` capture to contain characters other than lowercase letters and the default knausj symbols, you can tell Cursorless to be less aggressive with its normalization, so that it can allocate hats more efficiently. +The above setup will allow you to refer to any Unicode token, and is sufficient for most users. However, if you have overridden your `` capture to contain characters other than lowercase letters and the default symbols, you can tell Cursorless to be less aggressive with its normalization, so that it can allocate hats more efficiently. Note that this is not necessary in order to refer to these tokens; it just makes hat allocation slightly more efficient. ### Preserving case diff --git a/packages/common/src/cursorlessCommandIds.ts b/packages/common/src/cursorlessCommandIds.ts index cf0edd10aa..f1c69de9f5 100644 --- a/packages/common/src/cursorlessCommandIds.ts +++ b/packages/common/src/cursorlessCommandIds.ts @@ -78,7 +78,7 @@ export const cursorlessCommandDescriptions: Record< "Display the cursorless cheatsheet", ), ["cursorless.internal.updateCheatsheetDefaults"]: new HiddenCommand( - "Update the default values of the cheatsheet payload used on the website and for local development. Be sure to run this on stock knausj and cursorless.", + "Update the default values of the cheatsheet payload used on the website and for local development. Be sure to run this on stock community and cursorless.", ), ["cursorless.takeSnapshot"]: new HiddenCommand( "Take a snapshot of the current editor state", diff --git a/packages/cursorless-engine/src/tokenGraphemeSplitter/tokenGraphemeSplitter.ts b/packages/cursorless-engine/src/tokenGraphemeSplitter/tokenGraphemeSplitter.ts index 116bcd75e5..456f6f968a 100644 --- a/packages/cursorless-engine/src/tokenGraphemeSplitter/tokenGraphemeSplitter.ts +++ b/packages/cursorless-engine/src/tokenGraphemeSplitter/tokenGraphemeSplitter.ts @@ -8,7 +8,7 @@ import { import { matchAll } from "../util/regex"; /** - * A list of all symbols that are speakable by default in knausj. + * A list of all symbols that are speakable by default in community. */ const KNOWN_SYMBOLS = [ "!", diff --git a/packages/cursorless-vscode/package.json b/packages/cursorless-vscode/package.json index 584de90030..6257845faf 100644 --- a/packages/cursorless-vscode/package.json +++ b/packages/cursorless-vscode/package.json @@ -123,7 +123,7 @@ }, { "command": "cursorless.internal.updateCheatsheetDefaults", - "title": "Cursorless: Update the default values of the cheatsheet payload used on the website and for local development. Be sure to run this on stock knausj and cursorless.", + "title": "Cursorless: Update the default values of the cheatsheet payload used on the website and for local development. Be sure to run this on stock community and cursorless.", "enablement": "false" }, { From 5c069e39e717bff53696aeaf0ec12262ae6e79b1 Mon Sep 17 00:00:00 2001 From: Aaron Adams Date: Thu, 28 Sep 2023 22:29:23 +0800 Subject: [PATCH 43/54] Clarify extension dependency instructions for adding a new language (#1903) Following a discussion on Slack between Andreas and River about the Elixir PR tests failing, it seems like the docs needed to be updated to clarify how to add an extension dependency when adding support for a language type that vscode isn't natively aware of. ## Checklist - [ ] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [x] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [ ] I have not broken the cheatsheet --------- Co-authored-by: fidgetingbits Co-authored-by: Pokey Rule <755842+pokey@users.noreply.github.com> --- docs/contributing/CONTRIBUTING.md | 4 +++- docs/contributing/adding-a-new-language.md | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/contributing/CONTRIBUTING.md b/docs/contributing/CONTRIBUTING.md index 8ad61eac25..64b6f4e878 100644 --- a/docs/contributing/CONTRIBUTING.md +++ b/docs/contributing/CONTRIBUTING.md @@ -43,7 +43,9 @@ extension](#running--testing-extension-locally). You may also find the [VSCode A code --profile=cursorlessDevelopment --install-extension some.extension ``` - where `some.extension` is the id of the extension you'd like to install into the sandbox + where `some.extension` is the id of the extension you'd like to install into the sandbox. + + Note that if you are adding support for a new language that isn't in the default list of [language identifiers](https://code.visualstudio.com/docs/languages/identifiers#_known-language-identifiers) supported by VSCode, you may need to add an extension dependency. See [Adding a new language](./adding-a-new-language.md#2-ensure-file-type-is-supported-by-vscode) for more details. 6. Copy / symlink `cursorless-talon-dev` into your Talon user directory for some useful voice commands for developing Cursorless. diff --git a/docs/contributing/adding-a-new-language.md b/docs/contributing/adding-a-new-language.md index 860b817f29..b692992cb3 100644 --- a/docs/contributing/adding-a-new-language.md +++ b/docs/contributing/adding-a-new-language.md @@ -9,7 +9,11 @@ of a document. See the [docs](https://github.com/pokey/vscode-parse-tree/#adding-a-new-language) there for how to add support for a new parser -## 2. Define parse tree patterns in Cursorless +## 2. Ensure file type is supported by VSCode + +If you are adding support for a new language that isn't natively detected by VSCode, you will need to add the appropriate extension to the list of dependencies. The list of languages officially supported by VSCode is listed [in the VSCode docs](https://code.visualstudio.com/docs/languages/identifiers#_known-language-identifiers). If your language is in that list, you can skip this step and proceed to step 3. If your language is not in that list, you need to find a VSCode extension that adds support for your language, and add the id of the given extension to [`packages/common/src/extensionDependencies.ts`](../../packages/common/src/extensionDependencies.ts) and then re-run `pnpm init-vscode-sandbox` to ensure it is installed. If you do not do this you will encounter errors when attempting to execute cursorless commands in the next step. See [#1895](https://github.com/cursorless-dev/cursorless/issues/1895) for more info. + +## 3. Define parse tree patterns in Cursorless First a few notes / tips: From c2fb509c7a655b26f282a20d7a26a5e231b812ff Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 28 Sep 2023 16:36:41 +0200 Subject: [PATCH 44/54] Added argument to call action (#1900) `call air on bat` ## Checklist - [x] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [x] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [x] I have not broken the cheatsheet --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: David Vo Co-authored-by: Pokey Rule <755842+pokey@users.noreply.github.com> --- ...2023-09-addedArgumentTargetToCallAction.md | 6 +++ cursorless-talon/src/actions/actions.py | 6 ++- cursorless-talon/src/actions/call.py | 27 ++++++++---- .../src/cheatsheet/sections/actions.py | 16 +++++++- cursorless-talon/src/cursorless.talon | 3 ++ cursorless-talon/src/spoken_forms.json | 4 +- cursorless-talon/src/spoken_forms.py | 2 +- .../lib/sampleSpokenFormInfos/defaults.json | 14 +++++++ .../generateSpokenForm/generateSpokenForm.ts | 7 ++-- .../fixtures/recorded/actions/callFine.yml | 1 - .../recorded/actions/callFineOnBatt.yml | 41 +++++++++++++++++++ .../fixtures/recorded/actions/callVest.yml | 1 - .../recorded/actions/callVestOnCap.yml | 1 - 13 files changed, 108 insertions(+), 21 deletions(-) create mode 100644 changelog/2023-09-addedArgumentTargetToCallAction.md create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/callFineOnBatt.yml diff --git a/changelog/2023-09-addedArgumentTargetToCallAction.md b/changelog/2023-09-addedArgumentTargetToCallAction.md new file mode 100644 index 0000000000..bc5ff89dce --- /dev/null +++ b/changelog/2023-09-addedArgumentTargetToCallAction.md @@ -0,0 +1,6 @@ +--- +tags: [enhancement, talon] +pullRequest: 1900 +--- + +- Added optional second target to action `call` to specify argument. eg: `"call air on bat"`. diff --git a/cursorless-talon/src/actions/actions.py b/cursorless-talon/src/actions/actions.py index 55a8bd7763..93decf1e4f 100644 --- a/cursorless-talon/src/actions/actions.py +++ b/cursorless-talon/src/actions/actions.py @@ -8,7 +8,6 @@ ImplicitDestination, ) from .bring_move import BringMoveTargets -from .call import cursorless_call_action from .execute_command import cursorless_execute_command_action from .homophones import cursorless_homophones_action from .replace import cursorless_replace_action @@ -44,11 +43,11 @@ "wrap_action", "insert_snippet_action", "reformat_action", + "call_action", "experimental_action", ] callback_actions: dict[str, Callable[[CursorlessTarget], None]] = { - "callAsFunction": cursorless_call_action, "findInDocument": actions.user.private_cursorless_find, "nextHomophone": cursorless_homophones_action, } @@ -70,6 +69,7 @@ "{user.cursorless_simple_action} |" "{user.cursorless_experimental_action} |" "{user.cursorless_callback_action} |" + "{user.cursorless_call_action} |" "{user.cursorless_custom_action}" ) ) @@ -96,6 +96,8 @@ def cursorless_command(action_name: str, target: CursorlessTarget): actions.user.private_cursorless_bring_move( action_name, BringMoveTargets(target, ImplicitDestination()) ) + elif action_name == "callAsFunction": + actions.user.private_cursorless_call(target) elif action_name in no_wait_actions: action = {"name": action_name, "target": target} actions.user.private_cursorless_command_no_wait(action) diff --git a/cursorless-talon/src/actions/call.py b/cursorless-talon/src/actions/call.py index 083adf26b8..6601d7ba5f 100644 --- a/cursorless-talon/src/actions/call.py +++ b/cursorless-talon/src/actions/call.py @@ -1,13 +1,22 @@ -from talon import actions +from talon import Module, actions from ..targets.target_types import CursorlessTarget, ImplicitTarget +mod = Module() +mod.list("cursorless_call_action", desc="Cursorless call action") -def cursorless_call_action(target: CursorlessTarget): - actions.user.private_cursorless_command_and_wait( - { - "name": "callAsFunction", - "callee": target, - "argument": ImplicitTarget(), - } - ) + +@mod.action_class +class Actions: + def private_cursorless_call( + callee: CursorlessTarget, + argument: CursorlessTarget = ImplicitTarget(), + ): + """Execute Cursorless call action""" + actions.user.private_cursorless_command_and_wait( + { + "name": "callAsFunction", + "callee": callee, + "argument": argument, + } + ) diff --git a/cursorless-talon/src/cheatsheet/sections/actions.py b/cursorless-talon/src/cheatsheet/sections/actions.py index 37486d4ab1..b83c3e5ec8 100644 --- a/cursorless-talon/src/cheatsheet/sections/actions.py +++ b/cursorless-talon/src/cheatsheet/sections/actions.py @@ -12,6 +12,7 @@ def get_actions(): "moveToTarget", "swapTargets", "applyFormatter", + "callAsFunction", "wrapWithPairedDelimiter", "rewrap", "pasteFromClipboard", @@ -34,7 +35,6 @@ def get_actions(): "action", simple_actions, { - "callAsFunction": "Call on selection", "editNewLineAfter": "Edit new line/scope after", "editNewLineBefore": "Edit new line/scope before", }, @@ -101,6 +101,20 @@ def get_actions(): } ], }, + { + "id": "callAsFunction", + "type": "action", + "variations": [ + { + "spokenForm": f"{complex_actions['callAsFunction']} ", + "description": "Call on selection", + }, + { + "spokenForm": f"{complex_actions['callAsFunction']} on ", + "description": "Call on ", + }, + ], + }, { "id": "wrapWithPairedDelimiter", "type": "action", diff --git a/cursorless-talon/src/cursorless.talon b/cursorless-talon/src/cursorless.talon index 0f2f331dff..9b784f684c 100644 --- a/cursorless-talon/src/cursorless.talon +++ b/cursorless-talon/src/cursorless.talon @@ -18,6 +18,9 @@ tag: user.cursorless {user.cursorless_reformat_action} at : user.private_cursorless_reformat(cursorless_target, formatters) +{user.cursorless_call_action} on : + user.private_cursorless_call(cursorless_target_1, cursorless_target_2) + {user.cursorless_wrap_action} : user.private_cursorless_wrap_with_paired_delimiter(cursorless_wrap_action, cursorless_target, cursorless_wrapper_paired_delimiter) diff --git a/cursorless-talon/src/spoken_forms.json b/cursorless-talon/src/spoken_forms.json index db5acfab04..40a0fd16b1 100644 --- a/cursorless-talon/src/spoken_forms.json +++ b/cursorless-talon/src/spoken_forms.json @@ -43,7 +43,6 @@ "unfold": "unfoldRegion" }, "callback_action": { - "call": "callAsFunction", "scout": "findInDocument", "phones": "nextHomophone" }, @@ -55,7 +54,8 @@ "swap_action": { "swap": "swapTargets" }, "wrap_action": { "wrap": "wrapWithPairedDelimiter", "repack": "rewrap" }, "insert_snippet_action": { "snippet": "insertSnippet" }, - "reformat_action": { "format": "applyFormatter" } + "reformat_action": { "format": "applyFormatter" }, + "call_action": { "call": "callAsFunction" } }, "target_connectives.csv": { "range_connective": { diff --git a/cursorless-talon/src/spoken_forms.py b/cursorless-talon/src/spoken_forms.py index a0c804a992..16d53205f5 100644 --- a/cursorless-talon/src/spoken_forms.py +++ b/cursorless-talon/src/spoken_forms.py @@ -54,7 +54,7 @@ def update(): for disposable in disposables: disposable() - with open(JSON_FILE) as file: + with open(JSON_FILE, encoding="utf-8") as file: spoken_forms = json.load(file) handle_csv = auto_construct_defaults(spoken_forms, init_csv_and_watch_changes) diff --git a/packages/cheatsheet/src/lib/sampleSpokenFormInfos/defaults.json b/packages/cheatsheet/src/lib/sampleSpokenFormInfos/defaults.json index 014a9acc34..1c1e45f74a 100644 --- a/packages/cheatsheet/src/lib/sampleSpokenFormInfos/defaults.json +++ b/packages/cheatsheet/src/lib/sampleSpokenFormInfos/defaults.json @@ -21,6 +21,10 @@ { "spokenForm": "call ", "description": "Call on selection" + }, + { + "spokenForm": "call on ", + "description": "Call on " } ] }, @@ -1188,6 +1192,16 @@ } ] }, + { + "id": "command", + "type": "scopeType", + "variations": [ + { + "spokenForm": "command", + "description": "Command" + } + ] + }, { "id": "comment", "type": "scopeType", diff --git a/packages/cursorless-engine/src/generateSpokenForm/generateSpokenForm.ts b/packages/cursorless-engine/src/generateSpokenForm/generateSpokenForm.ts index 532485981e..34f5dda028 100644 --- a/packages/cursorless-engine/src/generateSpokenForm/generateSpokenForm.ts +++ b/packages/cursorless-engine/src/generateSpokenForm/generateSpokenForm.ts @@ -76,13 +76,14 @@ function generateSpokenFormComponents( ]; case "callAsFunction": - if (action.argument != null) { - throw new NoSpokenFormError(`Action '${action.name}' with argument`); + if (action.argument.type === "implicit") { + return [actions[action.name], targetToSpokenForm(action.callee)]; } return [ actions[action.name], targetToSpokenForm(action.callee), - // targetToSpokenForm(action.argument), + "on", + targetToSpokenForm(action.argument), ]; case "wrapWithPairedDelimiter": diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/callFine.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/callFine.yml index 1dd484ddee..ce28c2d8d3 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/callFine.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/callFine.yml @@ -7,7 +7,6 @@ command: - type: primitive mark: {type: decoratedSymbol, symbolColor: default, character: f} - {type: primitive, isImplicit: true} -spokenFormError: Action 'callAsFunction' with argument initialState: documentContents: |- foo; diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/callFineOnBatt.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/callFineOnBatt.yml new file mode 100644 index 0000000000..990596ee6a --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/callFineOnBatt.yml @@ -0,0 +1,41 @@ +languageId: plaintext +command: + version: 6 + spokenForm: call fine on bat + action: + name: callAsFunction + callee: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: f} + argument: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: b} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + foo + bar + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: + default.f: + start: {line: 0, character: 0} + end: {line: 0, character: 3} + default.b: + start: {line: 1, character: 0} + end: {line: 1, character: 3} +finalState: + documentContents: |- + foo + foo(bar) + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + thatMark: + - type: UntypedTarget + contentRange: + start: {line: 1, character: 0} + end: {line: 1, character: 8} + isReversed: false + hasExplicitRange: true diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/callVest.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/callVest.yml index dd90002c66..0796f6d295 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/callVest.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/callVest.yml @@ -7,7 +7,6 @@ command: - type: primitive mark: {type: decoratedSymbol, symbolColor: default, character: v} - {type: primitive, isImplicit: true} -spokenFormError: Action 'callAsFunction' with argument initialState: documentContents: | diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/callVestOnCap.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/callVestOnCap.yml index f7f9d4688a..cb557fe47c 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/callVestOnCap.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/callVestOnCap.yml @@ -8,7 +8,6 @@ command: mark: {type: decoratedSymbol, symbolColor: default, character: v} - type: primitive mark: {type: decoratedSymbol, symbolColor: default, character: c} -spokenFormError: Action 'callAsFunction' with argument initialState: documentContents: | From 5347894bdc49a0e6adb0e69384b8cf2fb8046c50 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 28 Sep 2023 15:39:05 +0100 Subject: [PATCH 45/54] Fix `boundedNonWhitespaceSequence` in cheatsheet (#1917) Fixes cheatsheet bug introduced in #1698 which only updated spoken form in defaults.json, which is used by cursorless.org/cheatsheet, but didn't update the code path that is used when user says "cursorless cheatsheet" ## Checklist - [ ] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [ ] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [ ] I have not broken the cheatsheet --- cursorless-talon/src/cheatsheet/sections/scopes.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cursorless-talon/src/cheatsheet/sections/scopes.py b/cursorless-talon/src/cheatsheet/sections/scopes.py index 49722a814a..b7133c9a72 100644 --- a/cursorless-talon/src/cheatsheet/sections/scopes.py +++ b/cursorless-talon/src/cheatsheet/sections/scopes.py @@ -5,5 +5,8 @@ def get_scopes(): return get_lists( ["scope_type"], "scopeType", - {"argumentOrParameter": "Argument"}, + { + "argumentOrParameter": "Argument", + "boundedNonWhitespaceSequence": "Non whitespace sequence stopped by surrounding pair delimeters", + }, ) From af14956605fd6c5261c3885bcbf04d34de27aa9a Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Tue, 3 Oct 2023 10:59:00 +0100 Subject: [PATCH 46/54] Migrate Python name and type scopes (#1922) ## Checklist - [ ] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [ ] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [ ] I have not broken the cheatsheet --- .../cursorless-engine/src/languages/python.ts | 11 --- .../languages/python/changeEveryName.yml | 33 +++++++++ .../languages/python/changeEveryName2.yml | 31 ++++++++ .../languages/python/changeEveryName3.yml | 29 ++++++++ .../languages/python/changeEveryType.yml | 29 ++++++++ .../languages/python/changeEveryType2.yml | 31 ++++++++ .../recorded/languages/python/changeType.yml | 27 +++++++ .../recorded/languages/python/changeType2.yml | 21 ++++++ .../recorded/languages/python/chuckName.yml | 31 ++++++++ queries/python.scm | 71 +++++++++++++++++-- 10 files changed, 298 insertions(+), 16 deletions(-) create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeEveryName.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeEveryName2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeEveryName3.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeEveryType.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeEveryType2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeType.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeType2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/chuckName.yml diff --git a/packages/cursorless-engine/src/languages/python.ts b/packages/cursorless-engine/src/languages/python.ts index 4ee79dd2cb..81346da0f0 100644 --- a/packages/cursorless-engine/src/languages/python.ts +++ b/packages/cursorless-engine/src/languages/python.ts @@ -69,17 +69,6 @@ const nodeMatchers: Partial< // Ternaries patternMatcher("conditional_expression[1]"), ), - type: leadingMatcher( - ["function_definition[return_type]", "*[type]"], - [":", "->"], - ), - name: [ - "assignment[left]", - "augmented_assignment[left]", - "typed_parameter.identifier!", - "parameters.identifier!", - "*[name]", - ], argumentOrParameter: cascadingMatcher( argumentMatcher("parameters", "argument_list"), matcher(patternFinder("call.generator_expression!"), childRangeSelector()), diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeEveryName.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeEveryName.yml new file mode 100644 index 0000000000..ac3806e7a6 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeEveryName.yml @@ -0,0 +1,33 @@ +languageId: python +command: + version: 6 + spokenForm: change every name + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: everyScope + scopeType: {type: name} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + aaa = bbb + aaa: str = bbb + aaa: str + selections: + - anchor: {line: 2, character: 8} + active: {line: 2, character: 8} + marks: {} +finalState: + documentContents: |2- + = bbb + : str = bbb + : str + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + - anchor: {line: 1, character: 0} + active: {line: 1, character: 0} + - anchor: {line: 2, character: 0} + active: {line: 2, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeEveryName2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeEveryName2.yml new file mode 100644 index 0000000000..0e49865560 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeEveryName2.yml @@ -0,0 +1,31 @@ +languageId: python +command: + version: 6 + spokenForm: change every name + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: everyScope + scopeType: {type: name} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + def aaa(): + bbb = ccc + ddd = eee + selections: + - anchor: {line: 2, character: 13} + active: {line: 2, character: 13} + marks: {} +finalState: + documentContents: |- + def aaa(): + = ccc + = eee + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} + - anchor: {line: 2, character: 4} + active: {line: 2, character: 4} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeEveryName3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeEveryName3.yml new file mode 100644 index 0000000000..7080bda266 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeEveryName3.yml @@ -0,0 +1,29 @@ +languageId: python +command: + version: 6 + spokenForm: change every name + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: everyScope + scopeType: {type: name} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + def aaa(bbb, ccc: str): + pass + selections: + - anchor: {line: 0, character: 21} + active: {line: 0, character: 21} + marks: {} +finalState: + documentContents: |- + def aaa(, : str): + pass + selections: + - anchor: {line: 0, character: 8} + active: {line: 0, character: 8} + - anchor: {line: 0, character: 10} + active: {line: 0, character: 10} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeEveryType.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeEveryType.yml new file mode 100644 index 0000000000..392c5070d0 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeEveryType.yml @@ -0,0 +1,29 @@ +languageId: python +command: + version: 6 + spokenForm: change every type + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: everyScope + scopeType: {type: type} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + def aaa(bbb, ccc: str, ddd: str): + pass + selections: + - anchor: {line: 0, character: 8} + active: {line: 0, character: 8} + marks: {} +finalState: + documentContents: |- + def aaa(bbb, ccc: , ddd: ): + pass + selections: + - anchor: {line: 0, character: 18} + active: {line: 0, character: 18} + - anchor: {line: 0, character: 25} + active: {line: 0, character: 25} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeEveryType2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeEveryType2.yml new file mode 100644 index 0000000000..bd66001275 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeEveryType2.yml @@ -0,0 +1,31 @@ +languageId: python +command: + version: 6 + spokenForm: change every type + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: everyScope + scopeType: {type: type} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + def aaa(): + bbb: str + ccc: str = "hello" + selections: + - anchor: {line: 2, character: 22} + active: {line: 2, character: 22} + marks: {} +finalState: + documentContents: |- + def aaa(): + bbb: + ccc: = "hello" + selections: + - anchor: {line: 1, character: 9} + active: {line: 1, character: 9} + - anchor: {line: 2, character: 9} + active: {line: 2, character: 9} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeType.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeType.yml new file mode 100644 index 0000000000..2dc0e88dde --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeType.yml @@ -0,0 +1,27 @@ +languageId: python +command: + version: 6 + spokenForm: change type + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: type} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + def aaa(bbb, ccc: int): + pass + selections: + - anchor: {line: 0, character: 13} + active: {line: 0, character: 13} + marks: {} +finalState: + documentContents: |- + def aaa(bbb, ccc: ): + pass + selections: + - anchor: {line: 0, character: 18} + active: {line: 0, character: 18} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeType2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeType2.yml new file mode 100644 index 0000000000..1508491688 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeType2.yml @@ -0,0 +1,21 @@ +languageId: python +command: + version: 6 + spokenForm: change type + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: type} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + def aaa(bbb, ccc: int): + pass + selections: + - anchor: {line: 0, character: 8} + active: {line: 0, character: 8} + marks: {} +thrownError: {name: NoContainingScopeError} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/chuckName.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/chuckName.yml new file mode 100644 index 0000000000..06b090aa96 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/chuckName.yml @@ -0,0 +1,31 @@ +languageId: python +command: + version: 6 + spokenForm: chuck name + action: + name: remove + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: name} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + aaa = bbb + aaa: str = bbb + selections: + - anchor: {line: 0, character: 9} + active: {line: 0, character: 9} + - anchor: {line: 1, character: 9} + active: {line: 1, character: 14} + marks: {} +finalState: + documentContents: |- + bbb + bbb + selections: + - anchor: {line: 0, character: 3} + active: {line: 0, character: 3} + - anchor: {line: 1, character: 0} + active: {line: 1, character: 3} diff --git a/queries/python.scm b/queries/python.scm index eb5e62aeb7..5673864cf3 100644 --- a/queries/python.scm +++ b/queries/python.scm @@ -38,14 +38,74 @@ right: (_) @value @_.leading.end.startOf ) @_.domain +;; value: ;;!! a /= 25 ;;! ^^ ;;! xxxxxx ;;! ------- +;; name: +;;!! a /= 25 +;;! ^ +;;! xxxxx +;;! ------- (augmented_assignment + left: (_) @name @name.trailing.start.endOf @value.leading.start.endOf + right: (_) @value @value.leading.end.startOf @name.trailing.end.startOf +) @_.domain + +;;!! a = 25 +;;! ^ +;;! xxxx +;;! ------ +;;!! a: int = 25 +;;! ^ +;;! xxxxxxxxx +;;! ----------- +(assignment + left: (_) @name @name.trailing.start.endOf + right: (_)? @name.trailing.end.startOf +) @_.domain + +(_ + name: (_) @name +) @_.domain + +;;!! def aaa(bbb): +;;! ^^^ +(parameters + (identifier) @name +) + +;;!! def aaa(bbb: str): +;;! ^^^ +;;! -------- +(typed_parameter + . + (_) @name +) @_.domain + +;; Matches any node at field `type` of its parent, with leading delimiter until +;; previous named node. For example: +;;!! aaa: str = "bbb"; +;;! ^^^ +;;! ----------------- +;;! xxxxx +(_ (_) @_.leading.start.endOf . - right: (_) @value @_.leading.end.startOf + type: (_) @type @_.leading.end.startOf +) @_.domain + +;;!! def aaa() -> str: +;;! ^^^ +;;! xxxxxxx +;;! [----------------- +;;!! pass +;;! --------] +(function_definition + (_) @_.leading.start.endOf + . + return_type: (_) @type @_.leading.end.startOf ) @_.domain ;;!! d = {"a": 1234} @@ -127,7 +187,7 @@ ) @class @className.domain (module) @className.iteration @class.iteration -(module) @statement.iteration +(module) @statement.iteration @value.iteration @name.iteration (module) @namedFunction.iteration @functionName.iteration (class_definition) @namedFunction.iteration @functionName.iteration @@ -138,7 +198,8 @@ ;;! ***** ;;!! c = 2 ;;! *****> -(block) @statement.iteration @value.iteration +(block) @statement.iteration @value.iteration @name.iteration +(block) @type.iteration ;;!! {"a": 1, "b": 2, "c": 3} ;;! ********************** @@ -150,6 +211,6 @@ ;;!! def func(a=0, b=1): ;;! ******** (parameters - "(" @value.iteration.start.endOf - ")" @value.iteration.end.startOf + "(" @value.iteration.start.endOf @name.iteration.start.endOf @type.iteration.start.endOf + ")" @value.iteration.end.startOf @name.iteration.end.startOf @type.iteration.end.startOf ) From 1164e2f3775e0f0026a60877ca6b676507d45c0d Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Tue, 3 Oct 2023 11:00:09 +0100 Subject: [PATCH 47/54] Migrate Typescript "value" to next gen; improve "name" (#1924) ## Checklist - [ ] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [ ] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [ ] I have not broken the cheatsheet --- .../TreeSitterQuery/QueryPredicateOperator.ts | 46 ++- .../queryPredicateOperators.ts | 15 + .../src/languages/typescript.ts | 33 --- .../languages/javascript/changeEveryValue.yml | 33 +++ .../javascript/changeEveryValue2.yml | 29 ++ .../languages/javascript/changeName.yml | 23 ++ .../languages/javascript/changeName2.yml | 105 +++++++ .../languages/javascript/changeName3.yml | 47 ++++ .../languages/javascript/changeValue.yml | 47 ++++ .../languages/javascript/changeValue2.yml | 109 ++++++++ .../languages/javascript/changeValue3.yml | 43 +++ .../languages/javascript/chuckName.yml | 67 +++++ .../languages/javascript/chuckName2.yml | 23 ++ .../languages/javascript/chuckName3.yml | 47 ++++ .../languages/javascript/chuckValue.yml | 47 ++++ .../languages/javascript/chuckValue2.yml | 101 +++++++ .../languages/jsx/changeEveryValue.yml | 25 ++ .../languages/jsx/changeEveryValue2.yml | 25 ++ .../recorded/languages/jsx/chuckValue.yml | 23 ++ queries/javascript.core.scm | 264 ++++++++++++++++-- queries/javascript.jsx.scm | 19 ++ 21 files changed, 1111 insertions(+), 60 deletions(-) create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeEveryValue.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeEveryValue2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeName.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeName2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeName3.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeValue.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeValue2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeValue3.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/chuckName.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/chuckName2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/chuckName3.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/chuckValue.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/chuckValue2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/jsx/changeEveryValue.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/jsx/changeEveryValue2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/jsx/chuckValue.yml diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/QueryPredicateOperator.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/QueryPredicateOperator.ts index c7601ad259..61cdb80318 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/QueryPredicateOperator.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/QueryPredicateOperator.ts @@ -47,6 +47,21 @@ export abstract class QueryPredicateOperator { ...args: AcceptFunctionArgs>> ): boolean; + /** + * Whether it is ok for a node argument to be missing. If true, then the + * operator will just accept the pattern if the given node is missing. If + * false, then the operator will throw an error if the node is missing. + * + * This is useful if we want to set some flag on a node, but only if it's + * present. + * + * @returns A boolean indicating whether it is ok for a node argument to be + * missing. + */ + protected allowMissingNode(): boolean { + return false; + } + /** * Given a list of operands, return a predicate function that can be used to * test whether a given match satisfies the predicate. @@ -62,8 +77,21 @@ export abstract class QueryPredicateOperator { return result.success ? { success: true, - predicate: (match: MutableQueryMatch) => - this.run(...this.constructAcceptArgs(result.data, match)), + predicate: (match: MutableQueryMatch) => { + try { + const acceptArgs = this.constructAcceptArgs(result.data, match); + return this.run(...acceptArgs); + } catch (err) { + if ( + err instanceof CaptureNotFoundError && + this.allowMissingNode() + ) { + return true; + } + + throw err; + } + }, } : { success: false, @@ -89,13 +117,7 @@ export abstract class QueryPredicateOperator { ); if (capture == null) { - // FIXME: We could allow some predicates to be forgiving, - // because it's possible to have a capture on an optional nodeInfo. - // In that case we'd prob just return `true` if any capture was - // `null`, but we should check that the given capture name - // appears statically in the given pattern. But we don't yet - // have a use case so let's leave it for now. - throw new Error(`Could not find capture ${operand.name}`); + throw new CaptureNotFoundError(operand.name); } return capture; @@ -117,3 +139,9 @@ interface FailedPredicateResult { } type PredicateResult = SuccessfulPredicateResult | FailedPredicateResult; + +class CaptureNotFoundError extends Error { + constructor(operandName: string) { + super(`Could not find capture ${operandName}`); + } +} diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts index c812fbd09d..6ac7210b79 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts @@ -150,10 +150,25 @@ class ShrinkToMatch extends QueryPredicateOperator { } } +/** + * Indicates that it is ok for multiple captures to have the same domain but + * different targets. For example, if we have the query `(#allow-multiple! + * @foo)`, then if we define the query so that `@foo` appears multiple times + * with the same domain but different targets, then the given domain will end up + * with multiple targets. The canonical example is `tags` in HTML / jsx. + * + * This operator is allowed to be applied to a capture that doesn't actually + * appear; ie we can make it so that we allow multiple if the capture appears in + * the pattern. + */ class AllowMultiple extends QueryPredicateOperator { name = "allow-multiple!" as const; schema = z.tuple([q.node]); + protected allowMissingNode(): boolean { + return true; + } + run(nodeInfo: MutableQueryCapture) { nodeInfo.allowMultiple = true; diff --git a/packages/cursorless-engine/src/languages/typescript.ts b/packages/cursorless-engine/src/languages/typescript.ts index 8498028e4d..9b7b2d3270 100644 --- a/packages/cursorless-engine/src/languages/typescript.ts +++ b/packages/cursorless-engine/src/languages/typescript.ts @@ -124,34 +124,6 @@ function typeMatcher(): NodeMatcher { }; } -function valueMatcher() { - const pFinder = patternFinder( - "assignment_expression[right]", - "augmented_assignment_expression[right]", - "*[value]", - "shorthand_property_identifier", - ); - return matcher( - (node: SyntaxNode) => - node.type === "jsx_attribute" ? node.lastChild : pFinder(node), - selectWithLeadingDelimiter( - ":", - "=", - "+=", - "-=", - "*=", - "/=", - "%=", - "**=", - "&=", - "|=", - "^=", - "<<=", - ">>=", - ), - ); -} - const mapTypes = ["object", "object_pattern"]; const listTypes = ["array", "array_pattern"]; @@ -171,11 +143,6 @@ const nodeMatchers: Partial< ], [":"], ), - value: cascadingMatcher( - valueMatcher(), - patternMatcher("return_statement.~return!"), - patternMatcher("yield_expression.~yield!"), - ), ifStatement: "if_statement", comment: "comment", regularExpression: "regex", diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeEveryValue.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeEveryValue.yml new file mode 100644 index 0000000000..b51f67cdf8 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeEveryValue.yml @@ -0,0 +1,33 @@ +languageId: javascript +command: + version: 6 + spokenForm: change every value + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: everyScope + scopeType: {type: value} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + function aaa() { + const bbb = 0; + const ccc = 0; + } + selections: + - anchor: {line: 2, character: 18} + active: {line: 2, character: 18} + marks: {} +finalState: + documentContents: |- + function aaa() { + const bbb = ; + const ccc = ; + } + selections: + - anchor: {line: 1, character: 16} + active: {line: 1, character: 16} + - anchor: {line: 2, character: 16} + active: {line: 2, character: 16} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeEveryValue2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeEveryValue2.yml new file mode 100644 index 0000000000..4eddf36914 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeEveryValue2.yml @@ -0,0 +1,29 @@ +languageId: javascript +command: + version: 6 + spokenForm: change every value + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: everyScope + scopeType: {type: value} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + const bbb = 0; + const ccc = 0; + selections: + - anchor: {line: 1, character: 14} + active: {line: 1, character: 14} + marks: {} +finalState: + documentContents: |- + const bbb = ; + const ccc = ; + selections: + - anchor: {line: 0, character: 12} + active: {line: 0, character: 12} + - anchor: {line: 1, character: 12} + active: {line: 1, character: 12} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeName.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeName.yml new file mode 100644 index 0000000000..b381255cd1 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeName.yml @@ -0,0 +1,23 @@ +languageId: javascript +command: + version: 6 + spokenForm: change name + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: name} + usePrePhraseSnapshot: true +initialState: + documentContents: const aaa = "bbb", ccc = "ddd"; + selections: + - anchor: {line: 0, character: 30} + active: {line: 0, character: 30} + marks: {} +finalState: + documentContents: const aaa = "bbb", = "ddd"; + selections: + - anchor: {line: 0, character: 19} + active: {line: 0, character: 19} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeName2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeName2.yml new file mode 100644 index 0000000000..da2076527f --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeName2.yml @@ -0,0 +1,105 @@ +languageId: javascript +command: + version: 6 + spokenForm: change name + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: name} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + const aaa = 0; + let bbb = 0; + var hhh = 0; + ccc = 0; + kkk += 1; + const ddd = 0, eee = 0; + let fff = 0, ggg = 0; + var iii = 0; + export const jjj = 0; + export let kkk = 0; + export var lll = 0; + export const mmm = 0, nnn = 0; + export let ooo = 0, ppp = 0; + selections: + - anchor: {line: 0, character: 14} + active: {line: 0, character: 14} + - anchor: {line: 1, character: 12} + active: {line: 1, character: 12} + - anchor: {line: 2, character: 12} + active: {line: 2, character: 12} + - anchor: {line: 3, character: 8} + active: {line: 3, character: 8} + - anchor: {line: 4, character: 9} + active: {line: 4, character: 9} + - anchor: {line: 5, character: 23} + active: {line: 5, character: 23} + - anchor: {line: 6, character: 21} + active: {line: 6, character: 21} + - anchor: {line: 7, character: 12} + active: {line: 7, character: 12} + - anchor: {line: 8, character: 21} + active: {line: 8, character: 21} + - anchor: {line: 9, character: 19} + active: {line: 9, character: 19} + - anchor: {line: 10, character: 19} + active: {line: 10, character: 19} + - anchor: {line: 11, character: 30} + active: {line: 11, character: 30} + - anchor: {line: 12, character: 28} + active: {line: 12, character: 28} + marks: {} +finalState: + documentContents: |- + const = 0; + let = 0; + var = 0; + = 0; + += 1; + const = 0, = 0; + let = 0, = 0; + var = 0; + export const = 0; + export let = 0; + export var = 0; + export const = 0, = 0; + export let = 0, = 0; + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} + - anchor: {line: 2, character: 4} + active: {line: 2, character: 4} + - anchor: {line: 3, character: 0} + active: {line: 3, character: 0} + - anchor: {line: 4, character: 0} + active: {line: 4, character: 0} + - anchor: {line: 5, character: 6} + active: {line: 5, character: 6} + - anchor: {line: 5, character: 12} + active: {line: 5, character: 12} + - anchor: {line: 6, character: 4} + active: {line: 6, character: 4} + - anchor: {line: 6, character: 10} + active: {line: 6, character: 10} + - anchor: {line: 7, character: 4} + active: {line: 7, character: 4} + - anchor: {line: 8, character: 13} + active: {line: 8, character: 13} + - anchor: {line: 9, character: 11} + active: {line: 9, character: 11} + - anchor: {line: 10, character: 11} + active: {line: 10, character: 11} + - anchor: {line: 11, character: 13} + active: {line: 11, character: 13} + - anchor: {line: 11, character: 19} + active: {line: 11, character: 19} + - anchor: {line: 12, character: 11} + active: {line: 12, character: 11} + - anchor: {line: 12, character: 17} + active: {line: 12, character: 17} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeName3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeName3.yml new file mode 100644 index 0000000000..1789821b20 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeName3.yml @@ -0,0 +1,47 @@ +languageId: javascript +command: + version: 6 + spokenForm: change name + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: name} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + hhh = 0, iii = 0, jjj=0; + lll += 1, mmm += 1, nnn += 1; + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + - anchor: {line: 0, character: 9} + active: {line: 0, character: 9} + - anchor: {line: 0, character: 18} + active: {line: 0, character: 18} + - anchor: {line: 1, character: 0} + active: {line: 1, character: 0} + - anchor: {line: 1, character: 10} + active: {line: 1, character: 10} + - anchor: {line: 1, character: 20} + active: {line: 1, character: 20} + marks: {} +finalState: + documentContents: |2- + = 0, = 0, =0; + += 1, += 1, += 1; + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} + - anchor: {line: 0, character: 12} + active: {line: 0, character: 12} + - anchor: {line: 1, character: 0} + active: {line: 1, character: 0} + - anchor: {line: 1, character: 7} + active: {line: 1, character: 7} + - anchor: {line: 1, character: 14} + active: {line: 1, character: 14} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeValue.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeValue.yml new file mode 100644 index 0000000000..4cb1bdd9b8 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeValue.yml @@ -0,0 +1,47 @@ +languageId: javascript +command: + version: 6 + spokenForm: change value + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: value} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + hhh = 0, iii = 0, jjj=0; + lll += 1, mmm += 1, nnn += 1; + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + - anchor: {line: 0, character: 9} + active: {line: 0, character: 9} + - anchor: {line: 0, character: 18} + active: {line: 0, character: 18} + - anchor: {line: 1, character: 0} + active: {line: 1, character: 0} + - anchor: {line: 1, character: 10} + active: {line: 1, character: 10} + - anchor: {line: 1, character: 20} + active: {line: 1, character: 20} + marks: {} +finalState: + documentContents: |- + hhh = , iii = , jjj=; + lll += , mmm += , nnn += ; + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} + - anchor: {line: 0, character: 14} + active: {line: 0, character: 14} + - anchor: {line: 0, character: 20} + active: {line: 0, character: 20} + - anchor: {line: 1, character: 7} + active: {line: 1, character: 7} + - anchor: {line: 1, character: 16} + active: {line: 1, character: 16} + - anchor: {line: 1, character: 25} + active: {line: 1, character: 25} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeValue2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeValue2.yml new file mode 100644 index 0000000000..3898f69f5a --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeValue2.yml @@ -0,0 +1,109 @@ +languageId: javascript +command: + version: 6 + spokenForm: change value + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: value} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + const aaa = 0; + let bbb = 0; + var hhh = 0; + ccc = 0; + kkk += 1; + const ddd = 0, eee = 0; + let fff = 0, ggg = 0; + var iii = 0; + let qqq; + var rrr; + export const jjj = 0; + export let kkk = 0; + export var lll = 0; + export const mmm = 0, nnn = 0; + export let ooo = 0, ppp = 0; + selections: + - anchor: {line: 0, character: 14} + active: {line: 0, character: 14} + - anchor: {line: 1, character: 12} + active: {line: 1, character: 12} + - anchor: {line: 2, character: 12} + active: {line: 2, character: 12} + - anchor: {line: 3, character: 8} + active: {line: 3, character: 8} + - anchor: {line: 4, character: 9} + active: {line: 4, character: 9} + - anchor: {line: 5, character: 23} + active: {line: 5, character: 23} + - anchor: {line: 6, character: 21} + active: {line: 6, character: 21} + - anchor: {line: 7, character: 12} + active: {line: 7, character: 12} + - anchor: {line: 10, character: 21} + active: {line: 10, character: 21} + - anchor: {line: 11, character: 19} + active: {line: 11, character: 19} + - anchor: {line: 12, character: 19} + active: {line: 12, character: 19} + - anchor: {line: 13, character: 30} + active: {line: 13, character: 30} + - anchor: {line: 14, character: 28} + active: {line: 14, character: 28} + marks: {} +finalState: + documentContents: |- + const aaa = ; + let bbb = ; + var hhh = ; + ccc = ; + kkk += ; + const ddd = , eee = ; + let fff = , ggg = ; + var iii = ; + let qqq; + var rrr; + export const jjj = ; + export let kkk = ; + export var lll = ; + export const mmm = , nnn = ; + export let ooo = , ppp = ; + selections: + - anchor: {line: 0, character: 12} + active: {line: 0, character: 12} + - anchor: {line: 1, character: 10} + active: {line: 1, character: 10} + - anchor: {line: 2, character: 10} + active: {line: 2, character: 10} + - anchor: {line: 3, character: 6} + active: {line: 3, character: 6} + - anchor: {line: 4, character: 7} + active: {line: 4, character: 7} + - anchor: {line: 5, character: 12} + active: {line: 5, character: 12} + - anchor: {line: 5, character: 20} + active: {line: 5, character: 20} + - anchor: {line: 6, character: 10} + active: {line: 6, character: 10} + - anchor: {line: 6, character: 18} + active: {line: 6, character: 18} + - anchor: {line: 7, character: 10} + active: {line: 7, character: 10} + - anchor: {line: 10, character: 19} + active: {line: 10, character: 19} + - anchor: {line: 11, character: 17} + active: {line: 11, character: 17} + - anchor: {line: 12, character: 17} + active: {line: 12, character: 17} + - anchor: {line: 13, character: 19} + active: {line: 13, character: 19} + - anchor: {line: 13, character: 27} + active: {line: 13, character: 27} + - anchor: {line: 14, character: 17} + active: {line: 14, character: 17} + - anchor: {line: 14, character: 25} + active: {line: 14, character: 25} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeValue3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeValue3.yml new file mode 100644 index 0000000000..ce24f8114e --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeValue3.yml @@ -0,0 +1,43 @@ +languageId: javascript +command: + version: 6 + spokenForm: change value + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: value} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + const ddd = 0, eee = 0; + let fff = 0, ggg = 0; + export const mmm = 0, nnn = 0; + export let ooo = 0, ppp = 0; + selections: + - anchor: {line: 0, character: 15} + active: {line: 0, character: 15} + - anchor: {line: 1, character: 13} + active: {line: 1, character: 13} + - anchor: {line: 2, character: 22} + active: {line: 2, character: 22} + - anchor: {line: 3, character: 20} + active: {line: 3, character: 20} + marks: {} +finalState: + documentContents: |- + const ddd = 0, eee = ; + let fff = 0, ggg = ; + export const mmm = 0, nnn = ; + export let ooo = 0, ppp = ; + selections: + - anchor: {line: 0, character: 21} + active: {line: 0, character: 21} + - anchor: {line: 1, character: 19} + active: {line: 1, character: 19} + - anchor: {line: 2, character: 28} + active: {line: 2, character: 28} + - anchor: {line: 3, character: 26} + active: {line: 3, character: 26} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/chuckName.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/chuckName.yml new file mode 100644 index 0000000000..f73bed4682 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/chuckName.yml @@ -0,0 +1,67 @@ +languageId: javascript +command: + version: 6 + spokenForm: chuck name + action: + name: remove + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: name} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + const aaa = 0; + let bbb = 0; + var hhh = 0; + ccc = 0; + kkk += 1; + const ddd = 0, eee = 0; + let fff = 0, ggg = 0; + var iii = 0; + selections: + - anchor: {line: 0, character: 14} + active: {line: 0, character: 14} + - anchor: {line: 1, character: 12} + active: {line: 1, character: 12} + - anchor: {line: 2, character: 12} + active: {line: 2, character: 12} + - anchor: {line: 3, character: 8} + active: {line: 3, character: 8} + - anchor: {line: 4, character: 9} + active: {line: 4, character: 9} + - anchor: {line: 5, character: 23} + active: {line: 5, character: 23} + - anchor: {line: 6, character: 21} + active: {line: 6, character: 21} + - anchor: {line: 7, character: 12} + active: {line: 7, character: 12} + marks: {} +finalState: + documentContents: |- + 0; + 0; + 0; + 0; + 1; + 0, 0; + 0, 0; + 0; + selections: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} + - anchor: {line: 1, character: 2} + active: {line: 1, character: 2} + - anchor: {line: 2, character: 2} + active: {line: 2, character: 2} + - anchor: {line: 3, character: 2} + active: {line: 3, character: 2} + - anchor: {line: 4, character: 2} + active: {line: 4, character: 2} + - anchor: {line: 5, character: 5} + active: {line: 5, character: 5} + - anchor: {line: 6, character: 5} + active: {line: 6, character: 5} + - anchor: {line: 7, character: 2} + active: {line: 7, character: 2} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/chuckName2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/chuckName2.yml new file mode 100644 index 0000000000..483074fd7b --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/chuckName2.yml @@ -0,0 +1,23 @@ +languageId: javascript +command: + version: 6 + spokenForm: chuck name + action: + name: remove + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: name} + usePrePhraseSnapshot: true +initialState: + documentContents: const aaa = "bbb", ccc = "ddd"; + selections: + - anchor: {line: 0, character: 13} + active: {line: 0, character: 13} + marks: {} +finalState: + documentContents: "\"bbb\", ccc = \"ddd\";" + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/chuckName3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/chuckName3.yml new file mode 100644 index 0000000000..04d8c2a893 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/chuckName3.yml @@ -0,0 +1,47 @@ +languageId: javascript +command: + version: 6 + spokenForm: chuck name + action: + name: remove + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: name} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + hhh = 0, iii = 0, jjj=0; + lll += 1, mmm += 1, nnn += 1; + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + - anchor: {line: 0, character: 9} + active: {line: 0, character: 9} + - anchor: {line: 0, character: 18} + active: {line: 0, character: 18} + - anchor: {line: 1, character: 0} + active: {line: 1, character: 0} + - anchor: {line: 1, character: 10} + active: {line: 1, character: 10} + - anchor: {line: 1, character: 20} + active: {line: 1, character: 20} + marks: {} +finalState: + documentContents: |- + 0, 0, 0; + 1, 1, 1; + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + - anchor: {line: 0, character: 3} + active: {line: 0, character: 3} + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} + - anchor: {line: 1, character: 0} + active: {line: 1, character: 0} + - anchor: {line: 1, character: 3} + active: {line: 1, character: 3} + - anchor: {line: 1, character: 6} + active: {line: 1, character: 6} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/chuckValue.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/chuckValue.yml new file mode 100644 index 0000000000..4dcfcd849e --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/chuckValue.yml @@ -0,0 +1,47 @@ +languageId: javascript +command: + version: 6 + spokenForm: chuck value + action: + name: remove + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: value} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + hhh = 0, iii = 0, jjj=0; + lll += 1, mmm += 1, nnn += 1; + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + - anchor: {line: 0, character: 9} + active: {line: 0, character: 9} + - anchor: {line: 0, character: 18} + active: {line: 0, character: 18} + - anchor: {line: 1, character: 0} + active: {line: 1, character: 0} + - anchor: {line: 1, character: 10} + active: {line: 1, character: 10} + - anchor: {line: 1, character: 20} + active: {line: 1, character: 20} + marks: {} +finalState: + documentContents: |- + hhh, iii, jjj; + lll, mmm, nnn; + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + - anchor: {line: 0, character: 5} + active: {line: 0, character: 5} + - anchor: {line: 0, character: 10} + active: {line: 0, character: 10} + - anchor: {line: 1, character: 0} + active: {line: 1, character: 0} + - anchor: {line: 1, character: 5} + active: {line: 1, character: 5} + - anchor: {line: 1, character: 10} + active: {line: 1, character: 10} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/chuckValue2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/chuckValue2.yml new file mode 100644 index 0000000000..71da6def1c --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/chuckValue2.yml @@ -0,0 +1,101 @@ +languageId: javascript +command: + version: 6 + spokenForm: chuck value + action: + name: remove + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: value} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + const aaa = 0; + let bbb = 0; + var hhh = 0; + ccc = 0; + kkk += 1; + const ddd = 0, eee = 0; + let fff = 0, ggg = 0; + var iii = 0; + let qqq; + var rrr; + export const jjj = 0; + export let kkk = 0; + export var lll = 0; + export const mmm = 0, nnn = 0; + export let ooo = 0, ppp = 0; + selections: + - anchor: {line: 0, character: 14} + active: {line: 0, character: 14} + - anchor: {line: 1, character: 12} + active: {line: 1, character: 12} + - anchor: {line: 2, character: 12} + active: {line: 2, character: 12} + - anchor: {line: 3, character: 8} + active: {line: 3, character: 8} + - anchor: {line: 4, character: 9} + active: {line: 4, character: 9} + - anchor: {line: 5, character: 23} + active: {line: 5, character: 23} + - anchor: {line: 6, character: 21} + active: {line: 6, character: 21} + - anchor: {line: 7, character: 12} + active: {line: 7, character: 12} + - anchor: {line: 10, character: 21} + active: {line: 10, character: 21} + - anchor: {line: 11, character: 19} + active: {line: 11, character: 19} + - anchor: {line: 12, character: 19} + active: {line: 12, character: 19} + - anchor: {line: 13, character: 30} + active: {line: 13, character: 30} + - anchor: {line: 14, character: 28} + active: {line: 14, character: 28} + marks: {} +finalState: + documentContents: |- + const aaa; + let bbb; + var hhh; + ccc; + kkk; + const ddd, eee; + let fff, ggg; + var iii; + let qqq; + var rrr; + export const jjj; + export let kkk; + export var lll; + export const mmm, nnn; + export let ooo, ppp; + selections: + - anchor: {line: 0, character: 10} + active: {line: 0, character: 10} + - anchor: {line: 1, character: 8} + active: {line: 1, character: 8} + - anchor: {line: 2, character: 8} + active: {line: 2, character: 8} + - anchor: {line: 3, character: 4} + active: {line: 3, character: 4} + - anchor: {line: 4, character: 4} + active: {line: 4, character: 4} + - anchor: {line: 5, character: 15} + active: {line: 5, character: 15} + - anchor: {line: 6, character: 13} + active: {line: 6, character: 13} + - anchor: {line: 7, character: 8} + active: {line: 7, character: 8} + - anchor: {line: 10, character: 17} + active: {line: 10, character: 17} + - anchor: {line: 11, character: 15} + active: {line: 11, character: 15} + - anchor: {line: 12, character: 15} + active: {line: 12, character: 15} + - anchor: {line: 13, character: 22} + active: {line: 13, character: 22} + - anchor: {line: 14, character: 20} + active: {line: 14, character: 20} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/jsx/changeEveryValue.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/jsx/changeEveryValue.yml new file mode 100644 index 0000000000..78deddbcac --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/jsx/changeEveryValue.yml @@ -0,0 +1,25 @@ +languageId: javascriptreact +command: + version: 6 + spokenForm: change every value + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: everyScope + scopeType: {type: value} + usePrePhraseSnapshot: true +initialState: + documentContents: + selections: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} + marks: {} +finalState: + documentContents: + selections: + - anchor: {line: 0, character: 9} + active: {line: 0, character: 9} + - anchor: {line: 0, character: 14} + active: {line: 0, character: 14} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/jsx/changeEveryValue2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/jsx/changeEveryValue2.yml new file mode 100644 index 0000000000..184f04dfb8 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/jsx/changeEveryValue2.yml @@ -0,0 +1,25 @@ +languageId: javascriptreact +command: + version: 6 + spokenForm: change every value + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: everyScope + scopeType: {type: value} + usePrePhraseSnapshot: true +initialState: + documentContents: + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} + marks: {} +finalState: + documentContents: + selections: + - anchor: {line: 0, character: 9} + active: {line: 0, character: 9} + - anchor: {line: 0, character: 14} + active: {line: 0, character: 14} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/jsx/chuckValue.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/jsx/chuckValue.yml new file mode 100644 index 0000000000..cf42f543fc --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/jsx/chuckValue.yml @@ -0,0 +1,23 @@ +languageId: javascriptreact +command: + version: 6 + spokenForm: chuck value + action: + name: remove + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: value} + usePrePhraseSnapshot: true +initialState: + documentContents: + selections: + - anchor: {line: 0, character: 5} + active: {line: 0, character: 5} + marks: {} +finalState: + documentContents: + selections: + - anchor: {line: 0, character: 5} + active: {line: 0, character: 5} diff --git a/queries/javascript.core.scm b/queries/javascript.core.scm index c7978670de..b5792c461c 100644 --- a/queries/javascript.core.scm +++ b/queries/javascript.core.scm @@ -34,21 +34,94 @@ ;; this full grandparent statement. ( [ + ;; name: ;;!! (const | let) foo = ...; - ;;! --------------^^^------- + ;;! ^^^ + ;;! xxxxxxxxxxxxxxxxxxxx + ;;! ------------------------ + ;; value: + ;;!! (const | let) foo = ...; + ;;! ^^^ + ;;! xxxxxx + ;;! ------------------------ (lexical_declaration + . (variable_declarator - name: (_) @name + name: (_) @name @name.removal.end.endOf @value.leading.start.endOf + value: (_)? @value @name.removal.end.startOf @value.leading.end.startOf ) ) + ;; name: + ;;!! var foo = ...; + ;;! ^^^ + ;;! xxxxxxxxxx + ;;! -------------- + ;; value: ;;!! var foo = ...; - ;;! ----^^^------- + ;;! ^^^ + ;;! xxxxxx + ;;! -------------- ;; Note that we can't merge this with the variable declaration above because ;; of https://github.com/tree-sitter/tree-sitter/issues/1442#issuecomment-1584628651 (variable_declaration + . (variable_declarator - name: (_) @name + name: (_) @name @name.removal.end.endOf @value.leading.start.endOf + value: (_)? @value @name.removal.end.startOf @value.leading.end.startOf + ) + ) + ] @_.domain @name.removal.start.startOf + (#not-parent-type? @_.domain export_statement) + + ;; Handle multiple variable declarators in one statement, eg + ;;!! (let | const | var) aaa = ..., ccc = ...; + ;;! --------------------^^^--------^^^------- + (#allow-multiple! @name) + (#allow-multiple! @value) +) + +;; Note that the same domains below will also have the first variable +;; as a target, but that is matched above +( + [ + ;; name: + ;;!! (const | let) aaa = 0, bbb = 0; + ;;! ^^^ + ;;! xxxxxx + ;;! ------------------------------- + ;; value: + ;;!! (const | let) aaa = 0, bbb = 0; + ;;! ^ + ;;! xxxx + ;;! ------------------------------- + (lexical_declaration + (variable_declarator) + . + (variable_declarator + name: (_) @name @name.trailing.start.endOf @value.leading.start.endOf + value: (_)? @value @name.trailing.end.startOf @value.leading.end.startOf + ) + ) + + ;; name: + ;;!! var aaa = 0, bbb = 0; + ;;! ^^^ + ;;! xxxxxx + ;;! --------------------- + ;; value: + ;;!! var aaa = 0, bbb = 0; + ;;! ^ + ;;! xxxx + ;;! --------------------- + ;; Note that we can't merge this with the variable declaration above because + ;; of https://github.com/tree-sitter/tree-sitter/issues/1442#issuecomment-1584628651 + (variable_declaration + (variable_declarator) + . + (variable_declarator + name: (_) @name @name.trailing.start.endOf @value.leading.start.endOf + value: (_)? @value @name.trailing.end.startOf @value.leading.end.startOf ) ) ] @_.domain @@ -58,15 +131,25 @@ ;;!! (let | const | var) aaa = ..., ccc = ...; ;;! --------------------^^^--------^^^------- (#allow-multiple! @name) + (#allow-multiple! @value) ) ( (export_statement (_ - ;;!! export [default] (let | const | var) foo = ...; - ;;! -------------------------------------^^^------- + ;; name: + ;;!! export (const | let | var) foo = ...; + ;;! ^^^ + ;;! xxxxxx + ;;! ------------------------------------- + ;; value: + ;;!! export (const | let | var) foo = 0; + ;;! ^ + ;;! xxxx + ;;! ----------------------------------- (variable_declarator - name: (_) @name + name: (_) @name @name.trailing.start.endOf @value.leading.start.endOf + value: (_)? @value @name.trailing.end.startOf @value.leading.end.startOf ) ) ) @_.domain @@ -75,24 +158,162 @@ ;;!! var foo = ..., bar = ...; ;;! ----^^^--------^^^------- (#allow-multiple! @name) + (#allow-multiple! @value) ) -;;!! foo += ...; -;;! ^^^-------- -(augmented_assignment_expression - left: (_) @name +;; name: +;;!! (const | let | var) aaa = 0, bbb = 0; +;;! ^^^ +;;! xxxxxxxxxxxxxxxxxxxxxxxxxx +;;! ------- +;; value: +;;!! (const | let | var) aaa = 0, bbb = 0; +;;! ^ +;;! xxxx +;;! ------- +( + (_ + . + (variable_declarator + name: (_) @name @name.removal.end.endOf @value.leading.start.endOf + value: (_)? @value @name.removal.end.startOf @value.leading.end.startOf + ) @_.domain + . + (variable_declarator) + ) @dummy @name.removal.start.startOf +) + +;; name: +;;!! (const | let | var) aaa = 0, bbb = 0; +;;!1 ^^^ +;;!1 xxxxxx +;;!1 ------- +;; value: +;;!! (const | let | var) aaa = 0, bbb = 0; +;;!1 ^ +;;!1 xxxx +;;!1 ------- +(_ + (variable_declarator) + . + (variable_declarator + name: (_) @name @name.trailing.start.endOf @value.leading.start.endOf + value: (_)? @value @name.trailing.end.startOf @value.leading.end.startOf + ) @_.domain +) @dummy + +(expression_statement + [ + ;; name: + ;;!! foo = 0; + ;;! ^^^ + ;;! xxxxxx + ;;! -------- + ;; value: + ;;!! foo = 0; + ;;! ^ + ;;! xxxx + ;;! -------- + (assignment_expression + left: (_) @name @value.leading.start.endOf @name.trailing.start.endOf + right: (_) @value @name.trailing.end.startOf @value.leading.end.startOf + ) + + ;; name: + ;;!! foo += 1; + ;;! ^^^ + ;;! xxxxxxx + ;;! --------- + ;; value: + ;;!! foo += 1; + ;;! ^ + ;;! xxxxx + ;;! --------- + (augmented_assignment_expression + left: (_) @name @value.leading.start.endOf @name.trailing.start.endOf + right: (_) @value @name.trailing.end.startOf @value.leading.end.startOf + ) + ] +) @_.domain + +( + [ + ;; name: + ;;!! aaa = 0, bbb = 0; + ;;!1 ^^^ + ;;!1 xxxxxx + ;;!1 ------- + ;;!2 ^^^ + ;;!2 xxxxxx + ;;!2 ------- + ;; value: + ;;!! aaa = 0, bbb = 0; + ;;!1 ^ + ;;!1 xxxx + ;;!1 ------- + ;;!2 ^ + ;;!2 xxxx + ;;!2 ------- + (assignment_expression + left: (_) @name @value.leading.start.endOf @name.trailing.start.endOf + right: (_) @value @name.trailing.end.startOf @value.leading.end.startOf + ) + + ;; name: + ;;!! aaa += 0, bbb += 0; + ;;!1 ^^^ + ;;!1 xxxxxxx + ;;!1 -------- + ;;!2 ^^^ + ;;!2 xxxxxxx + ;;!2 -------- + ;; value: + ;;!! aaa += 0, bbb += 0; + ;;!1 ^ + ;;!1 xxxxx + ;;!1 -------- + ;;!2 ^ + ;;!2 xxxxx + ;;!2 -------- + (augmented_assignment_expression + left: (_) @name @value.leading.start.endOf @name.trailing.start.endOf + right: (_) @value @name.trailing.end.startOf @value.leading.end.startOf + ) + ] @_.domain + + (#not-parent-type? @_.domain expression_statement) +) + +;; Match nodes at field `value` of their parent node, setting leading delimiter +;; to be the range until the previous named node +(_ + (_)? @value.leading.start.endOf + . + value: (_) @value @value.leading.end.startOf +) @_.domain + +;;!! const aaa = {bbb}; +;;! ^^^ +(shorthand_property_identifier) @value + +;;!! return 0; +;;! ^ +;;! --------- +(return_statement + (_) @value ) @_.domain -;;!! foo = ...; -;;! ^^^------- -(assignment_expression - left: (_) @name +;;!! yield 0; +;;! ^ +;;! -------- +(yield_expression + (_) @value ) @_.domain [ (program) (formal_parameters) -] @name.iteration +] @name.iteration @value.iteration ;; Treat interior of all bodies as iteration scopes for `name`, eg ;;!! function foo() { } @@ -100,8 +321,15 @@ (_ body: (_ . - "{" @name.iteration.start.endOf - "}" @name.iteration.end.startOf + "{" @name.iteration.start.endOf @value.iteration.start.endOf + "}" @name.iteration.end.startOf @value.iteration.end.startOf . ) ) + +;;!! const aaa = {bbb: 0, ccc: 0}; +;;! ************** +(object + "{" @value.iteration.start.endOf + "}" @value.iteration.end.startOf +) diff --git a/queries/javascript.jsx.scm b/queries/javascript.jsx.scm index f9b2cb44f9..129f9da20f 100644 --- a/queries/javascript.jsx.scm +++ b/queries/javascript.jsx.scm @@ -81,3 +81,22 @@ "<" @_.domain.start ">" @name.startOf @_.domain.end ) + +;;!! +;;! ^^^^^ +;;! xxxxxx +;;! --------- +(jsx_attribute + (_) @value.leading.start.endOf + (_) @value @value.leading.end.startOf +) @_.domain + +(jsx_self_closing_element + "<" @value.iteration.start.endOf + "/" @value.iteration.end.startOf +) + +(jsx_opening_element + "<" @value.iteration.start.endOf + ">" @value.iteration.end.startOf +) From 6230ecbc71c5951ae16c8740921b23d87b2c1dbb Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Tue, 3 Oct 2023 11:07:26 +0100 Subject: [PATCH 48/54] Forbid TODOs in the codebase (#1921) - Fixes https://github.com/cursorless-dev/cursorless/issues/1920 Here's a screenshot of a failed run: image ## Checklist - [-] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [-] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [-] I have not broken the cheatsheet --- .github/workflows/test.yml | 2 ++ .pre-commit-config.yaml | 1 + cursorless-talon-dev/src/default_vocabulary.py | 4 ---- packages/common/src/testUtil/TestCaseSnapshot.ts | 2 +- .../src/core/updateSelections/updateSelections.ts | 2 +- packages/cursorless-engine/src/languages/clojure.ts | 6 +++--- .../src/processTargets/targets/ParagraphTarget.ts | 2 +- .../src/suite/recorded.vscode.test.ts | 4 ++-- .../src/ide/vscode/VscodeFocusEditor.ts | 2 +- scripts/forbid-todo.sh | 13 +++++++++++++ 10 files changed, 25 insertions(+), 13 deletions(-) create mode 100755 scripts/forbid-todo.sh diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 40c5107e5c..e143c8cd4a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -72,3 +72,5 @@ jobs: name: dumps path: ${{ env.VSCODE_CRASH_DIR }} if: failure() + - name: Forbid TODOs + run: ./scripts/forbid-todo.sh diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 65150c9f46..9abdc72b13 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -32,6 +32,7 @@ repos: exclude_types: [svg] exclude: patches/.*\.patch - id: fix-byte-order-marker + - id: forbid-submodules - id: mixed-line-ending - id: trailing-whitespace # Trailing whitespace breaks yaml files if you use a multiline string diff --git a/cursorless-talon-dev/src/default_vocabulary.py b/cursorless-talon-dev/src/default_vocabulary.py index c2347135a6..6fada1a773 100644 --- a/cursorless-talon-dev/src/default_vocabulary.py +++ b/cursorless-talon-dev/src/default_vocabulary.py @@ -13,10 +13,6 @@ # https://github.com/talonhub/community/blob/9acb6c9659bb0c9b794a7b7126d025603b4ed726/core/keys/keys.py#L139C1-L171C2 punctuation_words = { - # TODO: I'm not sure why we need these, I think it has something to do with - # Dragon. Possibly it has been fixed by later improvements to talon? -rntz - # "`": "`", - # ",": ",", # <== these things "back tick": "`", "comma": ",", # Workaround for issue with conformer b-series; see #946 diff --git a/packages/common/src/testUtil/TestCaseSnapshot.ts b/packages/common/src/testUtil/TestCaseSnapshot.ts index 3c7649bc6b..dc300e1b8c 100644 --- a/packages/common/src/testUtil/TestCaseSnapshot.ts +++ b/packages/common/src/testUtil/TestCaseSnapshot.ts @@ -9,7 +9,7 @@ export type TestCaseSnapshot = { documentContents: string; selections: SelectionPlainObject[]; clipboard?: string; - // TODO Visible ranges are not asserted during testing, see: + // FIXME Visible ranges are not asserted during testing, see: // https://github.com/cursorless-dev/cursorless/issues/160 visibleRanges?: RangePlainObject[]; marks?: SerializedMarks; diff --git a/packages/cursorless-engine/src/core/updateSelections/updateSelections.ts b/packages/cursorless-engine/src/core/updateSelections/updateSelections.ts index c7f8c5246c..c2dded43e2 100644 --- a/packages/cursorless-engine/src/core/updateSelections/updateSelections.ts +++ b/packages/cursorless-engine/src/core/updateSelections/updateSelections.ts @@ -330,7 +330,7 @@ async function performEditsAndUpdateInternal( return selectionInfosToSelections(selectionInfoMatrix); } -// TODO: Remove this function if we don't end up using it for the next couple use cases, eg `that` mark and cursor history +// FIXME: Remove this function if we don't end up using it for the next couple use cases, eg `that` mark and cursor history export async function performEditsAndUpdateSelectionInfos( rangeUpdater: RangeUpdater, editor: EditableTextEditor, diff --git a/packages/cursorless-engine/src/languages/clojure.ts b/packages/cursorless-engine/src/languages/clojure.ts index bd00fb1d3b..9f1cb7338b 100644 --- a/packages/cursorless-engine/src/languages/clojure.ts +++ b/packages/cursorless-engine/src/languages/clojure.ts @@ -58,7 +58,7 @@ function indexNodeFinder( const nodeIndex = valueNodes.findIndex(({ id }) => id === node.id); if (nodeIndex === -1) { - // TODO: In the future we might conceivably try to handle saying "take + // FIXME: In the future we might conceivably try to handle saying "take // item" when the selection is inside a comment between the key and value return null; } @@ -154,7 +154,7 @@ const nodeMatchers: Partial< ), value: matcher(mapParityNodeFinder(1)), - // TODO: Handle formal parameters + // FIXME: Handle formal parameters argumentOrParameter: matcher( indexNodeFinder(patternFinder(functionCallPattern), (nodeIndex: number) => nodeIndex !== 0 ? nodeIndex : -1, @@ -176,7 +176,7 @@ const nodeMatchers: Partial< functionName: functionNameMatcher, - // TODO: Handle `let` declarations, defs, etc + // FIXME: Handle `let` declarations, defs, etc name: functionNameMatcher, anonymousFunction: cascadingMatcher( diff --git a/packages/cursorless-engine/src/processTargets/targets/ParagraphTarget.ts b/packages/cursorless-engine/src/processTargets/targets/ParagraphTarget.ts index 9d7f59da49..fddfadf176 100644 --- a/packages/cursorless-engine/src/processTargets/targets/ParagraphTarget.ts +++ b/packages/cursorless-engine/src/processTargets/targets/ParagraphTarget.ts @@ -34,7 +34,7 @@ export default class ParagraphTarget extends BaseTarget } getRemovalRange(): Range { - // TODO: In the future we could get rid of this function if {@link + // FIXME: In the future we could get rid of this function if {@link // getDelimitedSequenceRemovalRange} made a continuous range from the target // past its delimiter target and then used the removal range of that. const delimiterTarget = diff --git a/packages/cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts b/packages/cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts index db2f0658e3..62ad7e84c1 100644 --- a/packages/cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts +++ b/packages/cursorless-vscode-e2e/src/suite/recorded.vscode.test.ts @@ -68,7 +68,7 @@ async function runTest(file: string, spyIde: SpyIDE) { const fixture = yaml.load(buffer.toString()) as TestCaseFixtureLegacy; const excludeFields: ExcludableSnapshotField[] = []; - // TODO The snapshot gets messed up with timing issues when running the recorded tests + // FIXME The snapshot gets messed up with timing issues when running the recorded tests // "Couldn't find token default.a" const usePrePhraseSnapshot = false; @@ -180,7 +180,7 @@ async function runTest(file: string, spyIde: SpyIDE) { excludeFields.push("instanceReferenceMark"); } - // TODO Visible ranges are not asserted, see: + // FIXME Visible ranges are not asserted, see: // https://github.com/cursorless-dev/cursorless/issues/160 const { visibleRanges, ...resultState } = await takeSnapshot( excludeFields, diff --git a/packages/cursorless-vscode/src/ide/vscode/VscodeFocusEditor.ts b/packages/cursorless-vscode/src/ide/vscode/VscodeFocusEditor.ts index e336a0fbde..fdc847274a 100644 --- a/packages/cursorless-vscode/src/ide/vscode/VscodeFocusEditor.ts +++ b/packages/cursorless-vscode/src/ide/vscode/VscodeFocusEditor.ts @@ -53,7 +53,7 @@ function getViewColumn(editor: TextEditor): ViewColumn | undefined { if (editor.viewColumn != null) { return editor.viewColumn; } - // TODO: tabGroups is not available on older versions of vscode we still support. + // FIXME: tabGroups is not available on older versions of vscode we still support. // Remove any cast as soon as version is updated. if (semver.lt(version, "1.67.0")) { return undefined; diff --git a/scripts/forbid-todo.sh b/scripts/forbid-todo.sh new file mode 100755 index 0000000000..7a2220ef0b --- /dev/null +++ b/scripts/forbid-todo.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -euo pipefail +# Fail if there are any TODOs in the codebase + +# Find the string 'TODO' in all files tracked by git, excluding +# this file +TODOS_FOUND=$(git grep --color=always -nw TODO -- ':!scripts/forbid-todo.sh' || true) + +if [ -n "$TODOS_FOUND" ]; then + printf "\e[1;31mERROR: \e[0mTODOs found in codebase:\n" + printf '%s\n' "$TODOS_FOUND" + exit 1 +fi From 4a2091e4db53e03ea6311ce8910a9ab3e182f59b Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Tue, 3 Oct 2023 14:56:21 +0100 Subject: [PATCH 49/54] Support "name" and "value" in guard of Python for-each statements (#1919) - Depends on https://github.com/cursorless-dev/cursorless/pull/1922, which migrates `"name"` in Python - Depends on #1924, which migrates `value` in Typescript ## Checklist - [ ] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [ ] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [ ] I have not broken the cheatsheet --- .../languages/javascript/changeName4.yml | 23 ++++++++++++++++ .../languages/javascript/changeName5.yml | 19 +++++++++++++ .../languages/javascript/changeValue4.yml | 23 ++++++++++++++++ .../languages/javascript/changeValue5.yml | 19 +++++++++++++ .../recorded/languages/python/changeName.yml | 27 +++++++++++++++++++ .../recorded/languages/python/changeName2.yml | 21 +++++++++++++++ .../languages/python/changeValue3.yml | 27 +++++++++++++++++++ .../languages/python/changeValue4.yml | 21 +++++++++++++++ queries/javascript.core.scm | 14 ++++++++++ queries/python.scm | 14 ++++++++++ 10 files changed, 208 insertions(+) create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeName4.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeName5.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeValue4.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeValue5.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeName.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeName2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeValue3.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeValue4.yml diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeName4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeName4.yml new file mode 100644 index 0000000000..4335acfe1a --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeName4.yml @@ -0,0 +1,23 @@ +languageId: javascript +command: + version: 6 + spokenForm: change name + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: name} + usePrePhraseSnapshot: true +initialState: + documentContents: for (const aaa of bbb) {} + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: for (const of bbb) {} + selections: + - anchor: {line: 0, character: 11} + active: {line: 0, character: 11} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeName5.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeName5.yml new file mode 100644 index 0000000000..33b8d2db3c --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeName5.yml @@ -0,0 +1,19 @@ +languageId: javascript +command: + version: 6 + spokenForm: change name + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: name} + usePrePhraseSnapshot: true +initialState: + documentContents: for (const aaa of bbb) {} + selections: + - anchor: {line: 0, character: 24} + active: {line: 0, character: 24} + marks: {} +thrownError: {name: NoContainingScopeError} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeValue4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeValue4.yml new file mode 100644 index 0000000000..c5a2f03666 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeValue4.yml @@ -0,0 +1,23 @@ +languageId: javascript +command: + version: 6 + spokenForm: change value + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: value} + usePrePhraseSnapshot: true +initialState: + documentContents: for (const aaa of bbb) {} + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: for (const aaa of ) {} + selections: + - anchor: {line: 0, character: 18} + active: {line: 0, character: 18} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeValue5.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeValue5.yml new file mode 100644 index 0000000000..0e6fdfd58e --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/javascript/changeValue5.yml @@ -0,0 +1,19 @@ +languageId: javascript +command: + version: 6 + spokenForm: change value + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: value} + usePrePhraseSnapshot: true +initialState: + documentContents: for (const aaa of bbb) {} + selections: + - anchor: {line: 0, character: 24} + active: {line: 0, character: 24} + marks: {} +thrownError: {name: NoContainingScopeError} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeName.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeName.yml new file mode 100644 index 0000000000..2fea916c8c --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeName.yml @@ -0,0 +1,27 @@ +languageId: python +command: + version: 6 + spokenForm: change name + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: name} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + for aaa in bbb: + pass + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: |- + for in bbb: + pass + selections: + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeName2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeName2.yml new file mode 100644 index 0000000000..8b7ec1fde0 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeName2.yml @@ -0,0 +1,21 @@ +languageId: python +command: + version: 6 + spokenForm: change name + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: name} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + for aaa in bbb: + pass + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} + marks: {} +thrownError: {name: NoContainingScopeError} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeValue3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeValue3.yml new file mode 100644 index 0000000000..abebf29e89 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeValue3.yml @@ -0,0 +1,27 @@ +languageId: python +command: + version: 6 + spokenForm: change value + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: value} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + for aaa in bbb: + pass + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: |- + for aaa in : + pass + selections: + - anchor: {line: 0, character: 11} + active: {line: 0, character: 11} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeValue4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeValue4.yml new file mode 100644 index 0000000000..d2888bfc7f --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/python/changeValue4.yml @@ -0,0 +1,21 @@ +languageId: python +command: + version: 6 + spokenForm: change value + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: value} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + for aaa in bbb: + pass + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} + marks: {} +thrownError: {name: NoContainingScopeError} diff --git a/queries/javascript.core.scm b/queries/javascript.core.scm index b5792c461c..9f55bee43d 100644 --- a/queries/javascript.core.scm +++ b/queries/javascript.core.scm @@ -310,6 +310,20 @@ (_) @value ) @_.domain +;; name: +;;!! for (const aaa of bbb) {} +;;! ^^^ +;;! ---------------------- +;; value: +;;!! for (const aaa of bbb) {} +;;! ^^^ +;;! ---------------------- +(for_in_statement + left: (_) @name + right: (_) @value + ")" @_.domain.end.endOf +) @_.domain.start.startOf + [ (program) (formal_parameters) diff --git a/queries/python.scm b/queries/python.scm index 5673864cf3..180c675a59 100644 --- a/queries/python.scm +++ b/queries/python.scm @@ -140,6 +140,20 @@ (_) @value ) @_.domain +;; value: +;;!! for aaa in bbb: +;;! ^^^ +;;! --------------- +;; name: +;;!! for aaa in bbb: +;;! ^^^ +;;! --------------- +(for_statement + left: (_) @name + right: (_) @value + ":" @_.domain.end +) @_.domain.start.startOf + (comment) @comment @textFragment (string From 79b4d599a18325eea0a31e385aa28b75819372d1 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Tue, 3 Oct 2023 14:58:05 +0100 Subject: [PATCH 50/54] Migrate Typescript "type" to next-gen; fix value removal (#1925) Note that the first commit moves `typescript.scm` to `typescript.core.scm`, because we now have some patterns that shouldn't be defined for TSX (cast assertions eg `bar`). I made it its own commit because otherwise the diff is confusing. Might be easier to review https://github.com/cursorless-dev/cursorless/pull/1925/files/dbb4e5ea4135dad6bcff272f6bc85752a650013e..a2ced3b3ad14a27b4d47d344ddacfebba65c0915, which excludes that commit ## Checklist - [x] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [-] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [-] I have not broken the cheatsheet --- .../src/languages/typescript.ts | 83 +------ .../languages/typescript/changeEveryType.yml | 29 +++ .../languages/typescript/changeEveryType2.yml | 33 +++ .../languages/typescript/changeEveryType3.yml | 25 ++ .../languages/typescript/changeType7.yml | 113 +++++++++ .../languages/typescript/changeType8.yml | 43 ++++ .../languages/typescript/chuckName.yml | 55 +++++ .../languages/typescript/chuckType.yml | 103 +++++++++ .../languages/typescript/chuckType2.yml | 43 ++++ .../languages/typescript/chuckType3.yml | 23 ++ .../languages/typescript/chuckType4.yml | 31 +++ .../languages/typescript/chuckValue2.yml | 85 +++++++ .../languages/typescript/chuckValue3.yml | 43 ++++ queries/javascript.core.scm | 145 ++++++++---- queries/typescript.core.scm | 217 ++++++++++++++++++ queries/typescript.scm | 88 ++----- queries/typescriptreact.scm | 2 +- 17 files changed, 966 insertions(+), 195 deletions(-) create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeEveryType.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeEveryType2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeEveryType3.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType7.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType8.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckName.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckType.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckType2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckType3.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckType4.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckValue2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckValue3.yml create mode 100644 queries/typescript.core.scm diff --git a/packages/cursorless-engine/src/languages/typescript.ts b/packages/cursorless-engine/src/languages/typescript.ts index 9b7b2d3270..300cf8f393 100644 --- a/packages/cursorless-engine/src/languages/typescript.ts +++ b/packages/cursorless-engine/src/languages/typescript.ts @@ -1,10 +1,6 @@ import { SimpleScopeTypeType } from "@cursorless/common"; import type { SyntaxNode } from "web-tree-sitter"; -import { - NodeMatcher, - NodeMatcherAlternative, - SelectionWithEditor, -} from "../typings/Types"; +import { NodeMatcherAlternative, SelectionWithEditor } from "../typings/Types"; import { patternFinder } from "../util/nodeFinders"; import { argumentMatcher, @@ -20,9 +16,6 @@ import { extendForwardPastOptional, getNodeInternalRange, getNodeRange, - pairSelectionExtractor, - selectWithLeadingDelimiter, - simpleSelectionExtractor, unwrapSelectionExtractor, } from "../util/nodeSelectors"; import { branchMatcher } from "./branchMatcher"; @@ -69,61 +62,6 @@ const STATEMENT_TYPES = [ "with_statement", ]; -function typeMatcher(): NodeMatcher { - const delimiterSelector = selectWithLeadingDelimiter(":"); - return function (selection: SelectionWithEditor, node: SyntaxNode) { - if ( - node.parent?.type === "new_expression" && - node.type !== "new" && - node.type !== "arguments" - ) { - const identifierNode = node.parent.children.find( - (n) => n.type === "identifier", - ); - const argsNode = node.parent.children.find( - (n) => n.type === "type_arguments", - ); - if (identifierNode && argsNode) { - return [ - { - node, - selection: pairSelectionExtractor( - selection.editor, - identifierNode, - argsNode, - ), - }, - ]; - } else if (identifierNode) { - return [ - { - node: identifierNode, - selection: simpleSelectionExtractor( - selection.editor, - identifierNode, - ), - }, - ]; - } - } - - const typeAnnotationNode = node.children.find((child) => - ["type_annotation", "opting_type_annotation"].includes(child.type), - ); - const targetNode = typeAnnotationNode?.lastChild; - - if (targetNode) { - return [ - { - node: targetNode, - selection: delimiterSelector(selection.editor, targetNode), - }, - ]; - } - return null; - }; -} - const mapTypes = ["object", "object_pattern"]; const listTypes = ["array", "array_pattern"]; @@ -194,25 +132,6 @@ const nodeMatchers: Partial< "export_statement?.abstract_class_declaration", // export abstract class | abstract class "export_statement.class", // export default class ], - type: cascadingMatcher( - // Typed parameters, properties, and functions - typeMatcher(), - // matcher(findTypeNode, selectWithLeadingDelimiter(":")), - patternMatcher( - // Type alias/interface declarations - "export_statement?.type_alias_declaration", - "export_statement?.interface_declaration", - // as type declarations - "as_expression.generic_type!", - "as_expression.predefined_type!", - // satisfies type declaration - "satisfies_expression.generic_type!", - "satisfies_expression.predefined_type!", - // declaration - "type_assertion.type_arguments.generic_type!", - "type_assertion.type_arguments.predefined_type!", - ), - ), argumentOrParameter: argumentMatcher("formal_parameters", "arguments"), // XML, JSX attribute: ["jsx_attribute"], diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeEveryType.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeEveryType.yml new file mode 100644 index 0000000000..22dce551ce --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeEveryType.yml @@ -0,0 +1,29 @@ +languageId: typescript +command: + version: 6 + spokenForm: change every type + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: everyScope + scopeType: {type: type} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + const aaa: number = 0; + const bbb: number = 0; + selections: + - anchor: {line: 1, character: 22} + active: {line: 1, character: 22} + marks: {} +finalState: + documentContents: |- + const aaa: = 0; + const bbb: = 0; + selections: + - anchor: {line: 0, character: 11} + active: {line: 0, character: 11} + - anchor: {line: 1, character: 11} + active: {line: 1, character: 11} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeEveryType2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeEveryType2.yml new file mode 100644 index 0000000000..ea58ee0508 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeEveryType2.yml @@ -0,0 +1,33 @@ +languageId: typescript +command: + version: 6 + spokenForm: change every type + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: everyScope + scopeType: {type: type} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + function ccc() { + const aaa: number = 0; + const bbb: number = 0; + } + selections: + - anchor: {line: 2, character: 26} + active: {line: 2, character: 26} + marks: {} +finalState: + documentContents: |- + function ccc() { + const aaa: = 0; + const bbb: = 0; + } + selections: + - anchor: {line: 1, character: 15} + active: {line: 1, character: 15} + - anchor: {line: 2, character: 15} + active: {line: 2, character: 15} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeEveryType3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeEveryType3.yml new file mode 100644 index 0000000000..3aa8f6544f --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeEveryType3.yml @@ -0,0 +1,25 @@ +languageId: typescript +command: + version: 6 + spokenForm: change every type + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: everyScope + scopeType: {type: type} + usePrePhraseSnapshot: true +initialState: + documentContents: "function ccc(aaa: string, bbb: string) {}" + selections: + - anchor: {line: 0, character: 13} + active: {line: 0, character: 13} + marks: {} +finalState: + documentContents: "function ccc(aaa: , bbb: ) {}" + selections: + - anchor: {line: 0, character: 18} + active: {line: 0, character: 18} + - anchor: {line: 0, character: 25} + active: {line: 0, character: 25} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType7.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType7.yml new file mode 100644 index 0000000000..4209ffa5d4 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType7.yml @@ -0,0 +1,113 @@ +languageId: typescript +command: + version: 6 + spokenForm: change type + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: type} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + const aaa: number = 0; + let bbb: number = 0; + var hhh: number = 0; + const ddd: number = 0, eee: number = 0; + let fff: number = 0, ggg: number = 0; + var iii: number = 0; + export const jjj: number = 0; + export let kkk: number = 0; + export var lll: number = 0; + export const mmm: number = 0, nnn: number = 0; + export let ooo: number = 0, ppp: number = 0; + let qqq: number; + var rrr: number; + let sss: number, ttt: number; + selections: + - anchor: {line: 0, character: 22} + active: {line: 0, character: 22} + - anchor: {line: 1, character: 20} + active: {line: 1, character: 20} + - anchor: {line: 2, character: 20} + active: {line: 2, character: 20} + - anchor: {line: 3, character: 39} + active: {line: 3, character: 39} + - anchor: {line: 4, character: 37} + active: {line: 4, character: 37} + - anchor: {line: 5, character: 20} + active: {line: 5, character: 20} + - anchor: {line: 6, character: 29} + active: {line: 6, character: 29} + - anchor: {line: 7, character: 27} + active: {line: 7, character: 27} + - anchor: {line: 8, character: 27} + active: {line: 8, character: 27} + - anchor: {line: 9, character: 46} + active: {line: 9, character: 46} + - anchor: {line: 10, character: 44} + active: {line: 10, character: 44} + - anchor: {line: 11, character: 16} + active: {line: 11, character: 16} + - anchor: {line: 12, character: 16} + active: {line: 12, character: 16} + - anchor: {line: 13, character: 29} + active: {line: 13, character: 29} + marks: {} +finalState: + documentContents: |- + const aaa: = 0; + let bbb: = 0; + var hhh: = 0; + const ddd: = 0, eee: = 0; + let fff: = 0, ggg: = 0; + var iii: = 0; + export const jjj: = 0; + export let kkk: = 0; + export var lll: = 0; + export const mmm: = 0, nnn: = 0; + export let ooo: = 0, ppp: = 0; + let qqq: ; + var rrr: ; + let sss: , ttt: ; + selections: + - anchor: {line: 0, character: 11} + active: {line: 0, character: 11} + - anchor: {line: 1, character: 9} + active: {line: 1, character: 9} + - anchor: {line: 2, character: 9} + active: {line: 2, character: 9} + - anchor: {line: 3, character: 11} + active: {line: 3, character: 11} + - anchor: {line: 3, character: 22} + active: {line: 3, character: 22} + - anchor: {line: 4, character: 9} + active: {line: 4, character: 9} + - anchor: {line: 4, character: 20} + active: {line: 4, character: 20} + - anchor: {line: 5, character: 9} + active: {line: 5, character: 9} + - anchor: {line: 6, character: 18} + active: {line: 6, character: 18} + - anchor: {line: 7, character: 16} + active: {line: 7, character: 16} + - anchor: {line: 8, character: 16} + active: {line: 8, character: 16} + - anchor: {line: 9, character: 18} + active: {line: 9, character: 18} + - anchor: {line: 9, character: 29} + active: {line: 9, character: 29} + - anchor: {line: 10, character: 16} + active: {line: 10, character: 16} + - anchor: {line: 10, character: 27} + active: {line: 10, character: 27} + - anchor: {line: 11, character: 9} + active: {line: 11, character: 9} + - anchor: {line: 12, character: 9} + active: {line: 12, character: 9} + - anchor: {line: 13, character: 9} + active: {line: 13, character: 9} + - anchor: {line: 13, character: 16} + active: {line: 13, character: 16} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType8.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType8.yml new file mode 100644 index 0000000000..bceb2cd1ff --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeType8.yml @@ -0,0 +1,43 @@ +languageId: typescript +command: + version: 6 + spokenForm: change type + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: type} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + const ddd: number = 0, eee: number = 0; + let fff: number = 0, ggg: number = 0; + export const mmm: number = 0, nnn: number = 0; + export let ooo: number = 0, ppp: number = 0; + selections: + - anchor: {line: 0, character: 38} + active: {line: 0, character: 38} + - anchor: {line: 1, character: 36} + active: {line: 1, character: 36} + - anchor: {line: 2, character: 45} + active: {line: 2, character: 45} + - anchor: {line: 3, character: 43} + active: {line: 3, character: 43} + marks: {} +finalState: + documentContents: |- + const ddd: number = 0, eee: = 0; + let fff: number = 0, ggg: = 0; + export const mmm: number = 0, nnn: = 0; + export let ooo: number = 0, ppp: = 0; + selections: + - anchor: {line: 0, character: 28} + active: {line: 0, character: 28} + - anchor: {line: 1, character: 26} + active: {line: 1, character: 26} + - anchor: {line: 2, character: 35} + active: {line: 2, character: 35} + - anchor: {line: 3, character: 33} + active: {line: 3, character: 33} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckName.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckName.yml new file mode 100644 index 0000000000..98d3b3a125 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckName.yml @@ -0,0 +1,55 @@ +languageId: typescript +command: + version: 6 + spokenForm: chuck name + action: + name: remove + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: name} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + const aaa: number = 0; + let bbb: number = 0; + var hhh: number = 0; + const ddd: number = 0, eee: number = 0; + let fff: number = 0, ggg: number = 0; + var iii: number = 0; + selections: + - anchor: {line: 0, character: 22} + active: {line: 0, character: 22} + - anchor: {line: 1, character: 20} + active: {line: 1, character: 20} + - anchor: {line: 2, character: 20} + active: {line: 2, character: 20} + - anchor: {line: 3, character: 39} + active: {line: 3, character: 39} + - anchor: {line: 4, character: 37} + active: {line: 4, character: 37} + - anchor: {line: 5, character: 20} + active: {line: 5, character: 20} + marks: {} +finalState: + documentContents: |- + 0; + 0; + 0; + 0, 0; + 0, 0; + 0; + selections: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} + - anchor: {line: 1, character: 2} + active: {line: 1, character: 2} + - anchor: {line: 2, character: 2} + active: {line: 2, character: 2} + - anchor: {line: 3, character: 5} + active: {line: 3, character: 5} + - anchor: {line: 4, character: 5} + active: {line: 4, character: 5} + - anchor: {line: 5, character: 2} + active: {line: 5, character: 2} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckType.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckType.yml new file mode 100644 index 0000000000..b5384c9514 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckType.yml @@ -0,0 +1,103 @@ +languageId: typescript +command: + version: 6 + spokenForm: chuck type + action: + name: remove + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: type} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + const aaa: number = 0; + let bbb: number = 0; + var hhh: number = 0; + const ddd: number = 0, eee: number = 0; + let fff: number = 0, ggg: number = 0; + var iii: number = 0; + export const jjj: number = 0; + export let kkk: number = 0; + export var lll: number = 0; + export const mmm: number = 0, nnn: number = 0; + export let ooo: number = 0, ppp: number = 0; + let qqq: number; + var rrr: number; + let sss: number, ttt: number; + selections: + - anchor: {line: 0, character: 22} + active: {line: 0, character: 22} + - anchor: {line: 1, character: 20} + active: {line: 1, character: 20} + - anchor: {line: 2, character: 20} + active: {line: 2, character: 20} + - anchor: {line: 3, character: 39} + active: {line: 3, character: 39} + - anchor: {line: 4, character: 37} + active: {line: 4, character: 37} + - anchor: {line: 5, character: 20} + active: {line: 5, character: 20} + - anchor: {line: 6, character: 29} + active: {line: 6, character: 29} + - anchor: {line: 7, character: 27} + active: {line: 7, character: 27} + - anchor: {line: 8, character: 27} + active: {line: 8, character: 27} + - anchor: {line: 9, character: 46} + active: {line: 9, character: 46} + - anchor: {line: 10, character: 44} + active: {line: 10, character: 44} + - anchor: {line: 11, character: 16} + active: {line: 11, character: 16} + - anchor: {line: 12, character: 16} + active: {line: 12, character: 16} + - anchor: {line: 13, character: 29} + active: {line: 13, character: 29} + marks: {} +finalState: + documentContents: |- + const aaa = 0; + let bbb = 0; + var hhh = 0; + const ddd = 0, eee = 0; + let fff = 0, ggg = 0; + var iii = 0; + export const jjj = 0; + export let kkk = 0; + export var lll = 0; + export const mmm = 0, nnn = 0; + export let ooo = 0, ppp = 0; + let qqq; + var rrr; + let sss, ttt; + selections: + - anchor: {line: 0, character: 14} + active: {line: 0, character: 14} + - anchor: {line: 1, character: 12} + active: {line: 1, character: 12} + - anchor: {line: 2, character: 12} + active: {line: 2, character: 12} + - anchor: {line: 3, character: 23} + active: {line: 3, character: 23} + - anchor: {line: 4, character: 21} + active: {line: 4, character: 21} + - anchor: {line: 5, character: 12} + active: {line: 5, character: 12} + - anchor: {line: 6, character: 21} + active: {line: 6, character: 21} + - anchor: {line: 7, character: 19} + active: {line: 7, character: 19} + - anchor: {line: 8, character: 19} + active: {line: 8, character: 19} + - anchor: {line: 9, character: 30} + active: {line: 9, character: 30} + - anchor: {line: 10, character: 28} + active: {line: 10, character: 28} + - anchor: {line: 11, character: 8} + active: {line: 11, character: 8} + - anchor: {line: 12, character: 8} + active: {line: 12, character: 8} + - anchor: {line: 13, character: 13} + active: {line: 13, character: 13} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckType2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckType2.yml new file mode 100644 index 0000000000..bf4a234195 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckType2.yml @@ -0,0 +1,43 @@ +languageId: typescript +command: + version: 6 + spokenForm: chuck type + action: + name: remove + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: type} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + const ddd: number = 0, eee: number = 0; + let fff: number = 0, ggg: number = 0; + export const mmm: number = 0, nnn: number = 0; + export let ooo: number = 0, ppp: number = 0; + selections: + - anchor: {line: 0, character: 38} + active: {line: 0, character: 38} + - anchor: {line: 1, character: 36} + active: {line: 1, character: 36} + - anchor: {line: 2, character: 45} + active: {line: 2, character: 45} + - anchor: {line: 3, character: 43} + active: {line: 3, character: 43} + marks: {} +finalState: + documentContents: |- + const ddd: number = 0, eee = 0; + let fff: number = 0, ggg = 0; + export const mmm: number = 0, nnn = 0; + export let ooo: number = 0, ppp = 0; + selections: + - anchor: {line: 0, character: 30} + active: {line: 0, character: 30} + - anchor: {line: 1, character: 28} + active: {line: 1, character: 28} + - anchor: {line: 2, character: 37} + active: {line: 2, character: 37} + - anchor: {line: 3, character: 35} + active: {line: 3, character: 35} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckType3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckType3.yml new file mode 100644 index 0000000000..98c66b1bdf --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckType3.yml @@ -0,0 +1,23 @@ +languageId: typescript +command: + version: 6 + spokenForm: chuck type + action: + name: remove + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: type} + usePrePhraseSnapshot: true +initialState: + documentContents: const aaa = bbb; + selections: + - anchor: {line: 0, character: 23} + active: {line: 0, character: 23} + marks: {} +finalState: + documentContents: const aaa = bbb; + selections: + - anchor: {line: 0, character: 15} + active: {line: 0, character: 15} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckType4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckType4.yml new file mode 100644 index 0000000000..353fae2e9f --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckType4.yml @@ -0,0 +1,31 @@ +languageId: typescript +command: + version: 6 + spokenForm: chuck type + action: + name: remove + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: type} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + const aaa = bbb as string; + const ccc = ddd satisfies string; + selections: + - anchor: {line: 0, character: 12} + active: {line: 0, character: 12} + - anchor: {line: 1, character: 12} + active: {line: 1, character: 12} + marks: {} +finalState: + documentContents: |- + const aaa = bbb; + const ccc = ddd; + selections: + - anchor: {line: 0, character: 12} + active: {line: 0, character: 12} + - anchor: {line: 1, character: 12} + active: {line: 1, character: 12} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckValue2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckValue2.yml new file mode 100644 index 0000000000..d48d3e78e4 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckValue2.yml @@ -0,0 +1,85 @@ +languageId: typescript +command: + version: 6 + spokenForm: chuck value + action: + name: remove + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: value} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + const aaa: number = 0; + let bbb: number = 0; + var hhh: number = 0; + const ddd: number = 0, eee: number = 0; + let fff: number = 0, ggg: number = 0; + var iii: number = 0; + export const jjj: number = 0; + export let kkk: number = 0; + export var lll: number = 0; + export const mmm: number = 0, nnn: number = 0; + export let ooo: number = 0, ppp: number = 0; + selections: + - anchor: {line: 0, character: 22} + active: {line: 0, character: 22} + - anchor: {line: 1, character: 20} + active: {line: 1, character: 20} + - anchor: {line: 2, character: 20} + active: {line: 2, character: 20} + - anchor: {line: 3, character: 39} + active: {line: 3, character: 39} + - anchor: {line: 4, character: 37} + active: {line: 4, character: 37} + - anchor: {line: 5, character: 20} + active: {line: 5, character: 20} + - anchor: {line: 6, character: 29} + active: {line: 6, character: 29} + - anchor: {line: 7, character: 27} + active: {line: 7, character: 27} + - anchor: {line: 8, character: 27} + active: {line: 8, character: 27} + - anchor: {line: 9, character: 46} + active: {line: 9, character: 46} + - anchor: {line: 10, character: 44} + active: {line: 10, character: 44} + marks: {} +finalState: + documentContents: |- + const aaa: number; + let bbb: number; + var hhh: number; + const ddd: number, eee: number; + let fff: number, ggg: number; + var iii: number; + export const jjj: number; + export let kkk: number; + export var lll: number; + export const mmm: number, nnn: number; + export let ooo: number, ppp: number; + selections: + - anchor: {line: 0, character: 18} + active: {line: 0, character: 18} + - anchor: {line: 1, character: 16} + active: {line: 1, character: 16} + - anchor: {line: 2, character: 16} + active: {line: 2, character: 16} + - anchor: {line: 3, character: 31} + active: {line: 3, character: 31} + - anchor: {line: 4, character: 29} + active: {line: 4, character: 29} + - anchor: {line: 5, character: 16} + active: {line: 5, character: 16} + - anchor: {line: 6, character: 25} + active: {line: 6, character: 25} + - anchor: {line: 7, character: 23} + active: {line: 7, character: 23} + - anchor: {line: 8, character: 23} + active: {line: 8, character: 23} + - anchor: {line: 9, character: 38} + active: {line: 9, character: 38} + - anchor: {line: 10, character: 36} + active: {line: 10, character: 36} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckValue3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckValue3.yml new file mode 100644 index 0000000000..a282ba6508 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckValue3.yml @@ -0,0 +1,43 @@ +languageId: typescript +command: + version: 6 + spokenForm: chuck value + action: + name: remove + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: value} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + const ddd: number = 0, eee: number = 0; + let fff: number = 0, ggg: number = 0; + export const mmm: number = 0, nnn: number = 0; + export let ooo: number = 0, ppp: number = 0; + selections: + - anchor: {line: 0, character: 38} + active: {line: 0, character: 38} + - anchor: {line: 1, character: 36} + active: {line: 1, character: 36} + - anchor: {line: 2, character: 45} + active: {line: 2, character: 45} + - anchor: {line: 3, character: 43} + active: {line: 3, character: 43} + marks: {} +finalState: + documentContents: |- + const ddd: number = 0, eee: number; + let fff: number = 0, ggg: number; + export const mmm: number = 0, nnn: number; + export let ooo: number = 0, ppp: number; + selections: + - anchor: {line: 0, character: 34} + active: {line: 0, character: 34} + - anchor: {line: 1, character: 32} + active: {line: 1, character: 32} + - anchor: {line: 2, character: 41} + active: {line: 2, character: 41} + - anchor: {line: 3, character: 39} + active: {line: 3, character: 39} diff --git a/queries/javascript.core.scm b/queries/javascript.core.scm index 9f55bee43d..371adda430 100644 --- a/queries/javascript.core.scm +++ b/queries/javascript.core.scm @@ -47,8 +47,8 @@ (lexical_declaration . (variable_declarator - name: (_) @name @name.removal.end.endOf @value.leading.start.endOf - value: (_)? @value @name.removal.end.startOf @value.leading.end.startOf + name: (_) @name @name.removal.end.endOf + value: (_)? @name.removal.end.startOf ) ) @@ -67,8 +67,8 @@ (variable_declaration . (variable_declarator - name: (_) @name @name.removal.end.endOf @value.leading.start.endOf - value: (_)? @value @name.removal.end.startOf @value.leading.end.startOf + name: (_) @name @name.removal.end.endOf + value: (_)? @name.removal.end.startOf ) ) ] @_.domain @name.removal.start.startOf @@ -78,7 +78,6 @@ ;;!! (let | const | var) aaa = ..., ccc = ...; ;;! --------------------^^^--------^^^------- (#allow-multiple! @name) - (#allow-multiple! @value) ) ;; Note that the same domains below will also have the first variable @@ -99,8 +98,8 @@ (variable_declarator) . (variable_declarator - name: (_) @name @name.trailing.start.endOf @value.leading.start.endOf - value: (_)? @value @name.trailing.end.startOf @value.leading.end.startOf + name: (_) @name @name.trailing.start.endOf + value: (_)? @name.trailing.end.startOf ) ) @@ -120,8 +119,8 @@ (variable_declarator) . (variable_declarator - name: (_) @name @name.trailing.start.endOf @value.leading.start.endOf - value: (_)? @value @name.trailing.end.startOf @value.leading.end.startOf + name: (_) @name @name.trailing.start.endOf + value: (_)? @name.trailing.end.startOf ) ) ] @_.domain @@ -131,7 +130,6 @@ ;;!! (let | const | var) aaa = ..., ccc = ...; ;;! --------------------^^^--------^^^------- (#allow-multiple! @name) - (#allow-multiple! @value) ) ( @@ -148,8 +146,8 @@ ;;! xxxx ;;! ----------------------------------- (variable_declarator - name: (_) @name @name.trailing.start.endOf @value.leading.start.endOf - value: (_)? @value @name.trailing.end.startOf @value.leading.end.startOf + name: (_) @name @name.trailing.start.endOf + value: (_)? @name.trailing.end.startOf ) ) ) @_.domain @@ -158,7 +156,6 @@ ;;!! var foo = ..., bar = ...; ;;! ----^^^--------^^^------- (#allow-multiple! @name) - (#allow-multiple! @value) ) ;; name: @@ -171,36 +168,106 @@ ;;! ^ ;;! xxxx ;;! ------- -( - (_ - . - (variable_declarator - name: (_) @name @name.removal.end.endOf @value.leading.start.endOf - value: (_)? @value @name.removal.end.startOf @value.leading.end.startOf - ) @_.domain - . - (variable_declarator) - ) @dummy @name.removal.start.startOf -) +(_ + . + (variable_declarator + name: (_) @name @name.removal.end.endOf + value: (_)? @name.removal.end.startOf + ) @_.domain + . + (variable_declarator) +) @name.removal.start.startOf -;; name: -;;!! (const | let | var) aaa = 0, bbb = 0; -;;!1 ^^^ -;;!1 xxxxxx -;;!1 ------- -;; value: ;;!! (const | let | var) aaa = 0, bbb = 0; -;;!1 ^ -;;!1 xxxx -;;!1 ------- +;;! ^^^ +;;! xxxxxx +;;! ------- (_ (variable_declarator) . (variable_declarator - name: (_) @name @name.trailing.start.endOf @value.leading.start.endOf - value: (_)? @value @name.trailing.end.startOf @value.leading.end.startOf + name: (_) @name @name.trailing.start.endOf + value: (_)? @name.trailing.end.startOf ) @_.domain -) @dummy +) + +;; Special cases for `(let | const | var) foo = ...;` because the full statement +;; is actually a grandparent of the `name` node, so we want the domain to include +;; this full grandparent statement. +( + [ + ;;!! (const | let) aaa: Bbb = 0; + ;;! ^ + ;;! xxxx + ;;! --------------------------- + (lexical_declaration + (variable_declarator + (_) @value.leading.start.endOf + . + value: (_)? @value @value.leading.end.startOf + ) + ) + + ;;!! var aaa: Bbb = 0; + ;;! ^ + ;;! xxxx + ;;! ----------------- + ;; Note that we can't merge this with the variable declaration above because + ;; of https://github.com/tree-sitter/tree-sitter/issues/1442#issuecomment-1584628651 + (variable_declaration + (variable_declarator + (_) @value.leading.start.endOf + . + value: (_)? @value @value.leading.end.startOf + ) + ) + ] @_.domain + (#not-parent-type? @_.domain export_statement) + + ;; Handle multiple variable declarators in one statement, eg + ;;!! (let | const | var) aaa: Bbb = ..., ccc: Ddd = ...; + ;;! -------------------------------^^^-------------^^^- + (#allow-multiple! @value) +) + +( + (export_statement + (_ + ;;!! export (const | let | var) aaa: Bbb = 0; + ;;! ^ + ;;! xxxx + ;;! ---------------------------------------- + (variable_declarator + (_) @value.leading.start.endOf + . + value: (_)? @value @value.leading.end.startOf + ) + ) + ) @_.domain + + ;; Handle multiple variable declarators in one statement, eg + ;;!! export (let | const | var) aaa: Bbb = ..., ccc: Ddd = ...; + ;;! --------------------------------------^^^-------------^^^- + (#allow-multiple! @value) +) + +;;!! (const | let | var) aaa: Ccc = 0, bbb: Ddd = 0; +;;!1 ^ +;;!1 xxxx +;;!1 ------------ +;;!2 ^ +;;!2 xxxx +;;!2 ------------ +( + (_ + (variable_declarator + (_) @value.leading.start.endOf + . + value: (_)? @value @value.leading.end.startOf + ) @_.domain + ) @dummy + (#has-multiple-children-of-type? @dummy variable_declarator) +) (expression_statement [ @@ -327,7 +394,7 @@ [ (program) (formal_parameters) -] @name.iteration @value.iteration +] @name.iteration @value.iteration @type.iteration ;; Treat interior of all bodies as iteration scopes for `name`, eg ;;!! function foo() { } @@ -335,8 +402,8 @@ (_ body: (_ . - "{" @name.iteration.start.endOf @value.iteration.start.endOf - "}" @name.iteration.end.startOf @value.iteration.end.startOf + "{" @name.iteration.start.endOf @value.iteration.start.endOf @type.iteration.start.endOf + "}" @name.iteration.end.startOf @value.iteration.end.startOf @type.iteration.end.startOf . ) ) diff --git a/queries/typescript.core.scm b/queries/typescript.core.scm new file mode 100644 index 0000000000..12ec9443e8 --- /dev/null +++ b/queries/typescript.core.scm @@ -0,0 +1,217 @@ +;; Note that we don't just import `javascript.scm` because that includes +;; `javascript.jsx.scm`, and tree-sitter would complain because those node +;; types are not defined in the typescript grammar. + +;; import javascript.core.scm + +;;!! function aaa(bbb?: Ccc = "ddd") {} +;;! ^^^-------------- +(optional_parameter + (identifier) @name +) @_.domain + +;;!! function aaa(bbb: Ccc = "ddd") {} +;;! ^^^------------- +(required_parameter + (identifier) @name +) @_.domain + +;; Define these here because these node types don't exist in javascript. +( + [ + ;;!! class Foo { foo() {} } + ;;! ^^^^^^^^ + ;;!! interface Foo { foo(): void; } + ;;! ^^^^^^^^^^^^ + (method_signature + name: (_) @functionName @name + ) + + ;;!! class Foo { abstract foo(): void; } + ;;! ^^^^^^^^^^^^^^^^^^^^^ + (abstract_method_signature + name: (_) @functionName @name + ) + + ;;!! class Foo { + ;;!! (public | private | protected) foo = () => {}; + ;;! ^^^^^^^^^^^^^^^ + ;;!! (public | private | protected) foo = function() {}; + ;;! ^^^^^^^^^^^^^^^^^^^^ + ;;!! (public | private | protected) foo = function *() {}; + ;;! ^^^^^^^^^^^^^^^^^^^^^^ + ;;!! } + (public_field_definition + name: (_) @functionName + value: [ + (function + !name + ) + (generator_function + !name + ) + (arrow_function) + ] + ) + ] @namedFunction.start @functionName.domain.start @name.domain.start + . + ";"? @namedFunction.end @functionName.domain.end @name.domain.end +) + +( + ;;!! (public | private | protected) foo = ...; + ;;! -------------------------------^^^------- + (public_field_definition + name: (_) @name + ) @name.domain.start + . + ";"? @name.domain.end +) + +[ + (interface_declaration) + (object_type) +] @namedFunction.iteration @functionName.iteration + +;; Special cases for `(let | const | var) foo = ...;` because the full statement +;; is actually a grandparent of the `name` node, so we want the domain to include +;; this full grandparent statement. +( + [ + ;;!! (const | let) aaa: Bbb = 0; + ;;! ^^^ + ;;! xxxxx + ;;! --------------------------- + (lexical_declaration + (variable_declarator + type: (type_annotation + (_) @type + ) @type.removal + ) + ) + + ;;!! var aaa: Bbb = 0; + ;;! ^^^ + ;;! xxxxx + ;;! ----------------- + ;; Note that we can't merge this with the variable declaration above because + ;; of https://github.com/tree-sitter/tree-sitter/issues/1442#issuecomment-1584628651 + (variable_declaration + (variable_declarator + type: (type_annotation + (_) @type + ) @type.removal + ) + ) + ] @_.domain + (#not-parent-type? @_.domain export_statement) + + ;; Handle multiple variable declarators in one statement, eg + ;;!! (let | const | var) aaa: Bbb = ..., ccc: Ddd = ...; + ;;! -------------------------^^^-------------^^^------- + (#allow-multiple! @type) +) + +( + (export_statement + (_ + ;;!! export (const | let | var) aaa: Bbb = 0; + ;;! ^^^ + ;;! xxxxx + ;;! ---------------------------------------- + (variable_declarator + type: (type_annotation + (_) @type + ) @type.removal + ) + ) + ) @_.domain + + ;; Handle multiple variable declarators in one statement, eg + ;;!! export (let | const | var) aaa: Bbb = ..., ccc: Ddd = ...; + ;;! --------------------------------^^^-------------^^^------- + (#allow-multiple! @type) +) + +;;!! (const | let | var) aaa: Ccc = 0, bbb: Ddd = 0; +;;!1 ^^^ +;;!1 xxxxx +;;!1 ------------ +;;!2 ^^^ +;;!2 xxxxx +;;!2 ------------ +( + (_ + (variable_declarator + type: (type_annotation + (_) @type + ) @type.removal + ) @_.domain + ) @dummy + (#has-multiple-children-of-type? @dummy variable_declarator) +) + +;; Generic type matcher +( + (_ + [ + type: (_ + (_) @type + ) + return_type: (_ + (_) @type + ) + ] @type.removal + ) @_.domain + (#not-type? @_.domain variable_declarator) +) + +;;!! new Aaa() +;;! ^^^^^^^^ +(new_expression + constructor: (_) @type.start + type_arguments: (_)? @type.end +) + +;;!! interface Aaa {} +;;!! type Aaa = Bbb; +( + [ + (type_alias_declaration) + (interface_declaration) + ] @type + (#not-parent-type? @type export_statement) +) + +;;!! export interface Aaa {} +;;!! export type Aaa = Bbb; +(export_statement + [ + (type_alias_declaration) + (interface_declaration) + ] @type +) @_.domain + +;;!! aaa as Bbb +;;! ^^^ +;;! xxxxxxx +;;! ---------- +(as_expression + (_) @_.leading.start.endOf + [ + (generic_type) + (predefined_type) + ] @type @_.leading.end.startOf +) @_.domain + +;;!! aaa satisfies Bbb +;;! ^^^ +;;! xxxxxxxxxxxxxx +;;! ----------------- +(satisfies_expression + (_) @_.leading.start.endOf + [ + (generic_type) + (predefined_type) + ] @type @_.leading.end.startOf +) @_.domain diff --git a/queries/typescript.scm b/queries/typescript.scm index 9e724f2380..f885360bde 100644 --- a/queries/typescript.scm +++ b/queries/typescript.scm @@ -1,74 +1,16 @@ -;; Note that we don't just import `javascript.scm` because that includes -;; `javascript.jsx.scm`, and tree-sitter would complain because those node -;; types are not defined in the typescript grammar. - -;; import javascript.core.scm - -;;!! function aaa(bbb?: Ccc = "ddd") {} -;;! ^^^-------------- -(optional_parameter - (identifier) @name +;; import typescript.core.scm + +;; Define this here because type assertions don't exist in TSX because +;; they are not valid JSX. +;;!! bbb +;;! ^^^ +;;! xxxxx +;;! -------- +(type_assertion + (type_arguments + [ + (generic_type) + (predefined_type) + ] @type + ) @_.removal ) @_.domain - -;;!! function aaa(bbb: Ccc = "ddd") {} -;;! ^^^------------- -(required_parameter - (identifier) @name -) @_.domain - -;; Define these here because these node types don't exist in javascript. -( - [ - ;;!! class Foo { foo() {} } - ;;! ^^^^^^^^ - ;;!! interface Foo { foo(): void; } - ;;! ^^^^^^^^^^^^ - (method_signature - name: (_) @functionName @name - ) - - ;;!! class Foo { abstract foo(): void; } - ;;! ^^^^^^^^^^^^^^^^^^^^^ - (abstract_method_signature - name: (_) @functionName @name - ) - - ;;!! class Foo { - ;;!! (public | private | protected) foo = () => {}; - ;;! ^^^^^^^^^^^^^^^ - ;;!! (public | private | protected) foo = function() {}; - ;;! ^^^^^^^^^^^^^^^^^^^^ - ;;!! (public | private | protected) foo = function *() {}; - ;;! ^^^^^^^^^^^^^^^^^^^^^^ - ;;!! } - (public_field_definition - name: (_) @functionName - value: [ - (function - !name - ) - (generator_function - !name - ) - (arrow_function) - ] - ) - ] @namedFunction.start @functionName.domain.start @name.domain.start - . - ";"? @namedFunction.end @functionName.domain.end @name.domain.end -) - -( - ;;!! (public | private | protected) foo = ...; - ;;! -------------------------------^^^------- - (public_field_definition - name: (_) @name - ) @name.domain.start - . - ";"? @name.domain.end -) - -[ - (interface_declaration) - (object_type) -] @namedFunction.iteration @functionName.iteration diff --git a/queries/typescriptreact.scm b/queries/typescriptreact.scm index 934357070c..b01d30a0f9 100644 --- a/queries/typescriptreact.scm +++ b/queries/typescriptreact.scm @@ -1,2 +1,2 @@ ;; import javascript.jsx.scm -;; import typescript.scm +;; import typescript.core.scm From f1ba894f95548cf155b020a9b62e44cf2f2f6f5e Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 5 Oct 2023 06:22:38 +0100 Subject: [PATCH 51/54] Fix value scope nesting in Typescript (#1929) We only want nested value scopes if there is more than one declarator in a statement. Before: image After: image Unfortunately we have no good way to test this change until we have https://github.com/cursorless-dev/cursorless/issues/1524 **Edit**: I found a way to test it, but it's a bit hacky. ## Checklist - [ ] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [ ] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [ ] I have not broken the cheatsheet --- .../languages/typescript/changeNextValue.yml | 30 +++++++++++++++++++ queries/javascript.core.scm | 13 ++++---- 2 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeNextValue.yml diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeNextValue.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeNextValue.yml new file mode 100644 index 0000000000..3abb6a3554 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeNextValue.yml @@ -0,0 +1,30 @@ +languageId: typescript +command: + version: 6 + spokenForm: change next value + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: relativeScope + scopeType: {type: value} + offset: 1 + length: 1 + direction: forward + usePrePhraseSnapshot: true +initialState: + documentContents: |- + const aaa = 0; + const bbb = 0; + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: |- + const aaa = 0; + const bbb = ; + selections: + - anchor: {line: 1, character: 12} + active: {line: 1, character: 12} diff --git a/queries/javascript.core.scm b/queries/javascript.core.scm index 371adda430..920524256d 100644 --- a/queries/javascript.core.scm +++ b/queries/javascript.core.scm @@ -353,11 +353,14 @@ ;; Match nodes at field `value` of their parent node, setting leading delimiter ;; to be the range until the previous named node -(_ - (_)? @value.leading.start.endOf - . - value: (_) @value @value.leading.end.startOf -) @_.domain +( + (_ + (_)? @value.leading.start.endOf + . + value: (_) @value @value.leading.end.startOf + ) @_.domain + (#not-type? @_.domain variable_declarator) +) ;;!! const aaa = {bbb}; ;;! ^^^ From 4ec5b952666a042dc7d41ed738ca9cfd90fc0d43 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 5 Oct 2023 06:39:31 +0100 Subject: [PATCH 52/54] Include `@` in `name` for `scm` language (#1927) Fixes https://github.com/cursorless-dev/cursorless/issues/1923 ## Checklist - [ ] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [ ] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [ ] I have not broken the cheatsheet --- .../languages/scm/changeEveryName.yml | 10 +++---- .../recorded/languages/scm/changeName.yml | 6 ++-- .../recorded/languages/scm/changeName2.yml | 6 ++-- .../recorded/languages/scm/changeName3.yml | 6 ++-- .../recorded/languages/scm/chuckName.yml | 2 +- .../recorded/languages/scm/chuckName2.yml | 6 ++-- .../recorded/languages/scm/clearEveryName.yml | 6 ++-- .../languages/scm/clearEveryName2.yml | 6 ++-- .../languages/scm/clearEveryName3.yml | 6 ++-- .../recorded/languages/scm/clearName.yml | 6 ++-- .../recorded/languages/scm/clearName2.yml | 6 ++-- .../recorded/languages/scm/clearName3.yml | 6 ++-- .../recorded/languages/scm/clearName4.yml | 6 ++-- .../recorded/languages/scm/clearName5.yml | 6 ++-- .../recorded/languages/scm/clearName6.yml | 6 ++-- .../recorded/languages/scm/clearName7.yml | 6 ++-- .../recorded/languages/scm/drinkName.yml | 6 ++-- .../recorded/languages/scm/pourName.yml | 6 ++-- .../recorded/languages/scm/pourName2.yml | 6 ++-- .../recorded/languages/scm/takeName.yml | 2 +- queries/scm.name.scm | 28 ++++--------------- 21 files changed, 64 insertions(+), 80 deletions(-) diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/changeEveryName.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/changeEveryName.yml index b30a3a3753..b6c78bcf71 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/changeEveryName.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/changeEveryName.yml @@ -17,11 +17,11 @@ initialState: active: {line: 0, character: 0} marks: {} finalState: - documentContents: (aaa) @ @ @ + documentContents: "(aaa) " selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} - anchor: {line: 0, character: 7} active: {line: 0, character: 7} - - anchor: {line: 0, character: 9} - active: {line: 0, character: 9} - - anchor: {line: 0, character: 11} - active: {line: 0, character: 11} + - anchor: {line: 0, character: 8} + active: {line: 0, character: 8} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/changeName.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/changeName.yml index 13e3bcd9df..007b4a4c97 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/changeName.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/changeName.yml @@ -17,7 +17,7 @@ initialState: active: {line: 0, character: 0} marks: {} finalState: - documentContents: (aaa) @ + documentContents: "(aaa) " selections: - - anchor: {line: 0, character: 7} - active: {line: 0, character: 7} + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/changeName2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/changeName2.yml index 8d70cd725a..3cbfcd6846 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/changeName2.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/changeName2.yml @@ -17,7 +17,7 @@ initialState: active: {line: 0, character: 0} marks: {} finalState: - documentContents: "eee: (aaa) @" + documentContents: "eee: (aaa) " selections: - - anchor: {line: 0, character: 12} - active: {line: 0, character: 12} + - anchor: {line: 0, character: 11} + active: {line: 0, character: 11} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/changeName3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/changeName3.yml index 8406b97e0f..0c1497b6d4 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/changeName3.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/changeName3.yml @@ -17,7 +17,7 @@ initialState: active: {line: 0, character: 0} marks: {} finalState: - documentContents: "eee: _ @" + documentContents: "eee: _ " selections: - - anchor: {line: 0, character: 8} - active: {line: 0, character: 8} + - anchor: {line: 0, character: 7} + active: {line: 0, character: 7} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/chuckName.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/chuckName.yml index 3a07b9fc75..110e686430 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/chuckName.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/chuckName.yml @@ -17,7 +17,7 @@ initialState: active: {line: 0, character: 0} marks: {} finalState: - documentContents: "(aaa) " + documentContents: (aaa) selections: - anchor: {line: 0, character: 0} active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/chuckName2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/chuckName2.yml index 48eca2ec19..3fc43eed23 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/chuckName2.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/chuckName2.yml @@ -17,7 +17,7 @@ initialState: active: {line: 0, character: 20} marks: {} finalState: - documentContents: "(aaa) @bbb @ccc " + documentContents: (aaa) @bbb @ccc selections: - - anchor: {line: 0, character: 16} - active: {line: 0, character: 16} + - anchor: {line: 0, character: 15} + active: {line: 0, character: 15} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearEveryName.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearEveryName.yml index 3d7ca3e4da..aec931039c 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearEveryName.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearEveryName.yml @@ -16,9 +16,9 @@ initialState: active: {line: 0, character: 0} marks: {} finalState: - documentContents: (aaa) @ @ + documentContents: "(aaa) " selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} - anchor: {line: 0, character: 7} active: {line: 0, character: 7} - - anchor: {line: 0, character: 9} - active: {line: 0, character: 9} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearEveryName2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearEveryName2.yml index 05f69f2028..c5357a054f 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearEveryName2.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearEveryName2.yml @@ -16,9 +16,9 @@ initialState: active: {line: 0, character: 0} marks: {} finalState: - documentContents: "[(aaa) (bbb)] @ @" + documentContents: "[(aaa) (bbb)] " selections: + - anchor: {line: 0, character: 14} + active: {line: 0, character: 14} - anchor: {line: 0, character: 15} active: {line: 0, character: 15} - - anchor: {line: 0, character: 17} - active: {line: 0, character: 17} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearEveryName3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearEveryName3.yml index 4929f597fa..9e7b369b8f 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearEveryName3.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearEveryName3.yml @@ -21,10 +21,10 @@ initialState: finalState: documentContents: |- (anonymous_node - name: (_) @ @ + name: (_) ) selections: + - anchor: {line: 1, character: 12} + active: {line: 1, character: 12} - anchor: {line: 1, character: 13} active: {line: 1, character: 13} - - anchor: {line: 1, character: 15} - active: {line: 1, character: 15} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName.yml index b9fb035b80..c256a34303 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName.yml @@ -16,7 +16,7 @@ initialState: active: {line: 0, character: 0} marks: {} finalState: - documentContents: (aaa) @ + documentContents: "(aaa) " selections: - - anchor: {line: 0, character: 7} - active: {line: 0, character: 7} + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName2.yml index 93d278995a..0d162e078d 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName2.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName2.yml @@ -16,7 +16,7 @@ initialState: active: {line: 0, character: 11} marks: {} finalState: - documentContents: (aaa) @bbb @ + documentContents: "(aaa) @bbb " selections: - - anchor: {line: 0, character: 12} - active: {line: 0, character: 12} + - anchor: {line: 0, character: 11} + active: {line: 0, character: 11} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName3.yml index 6483c5f1a4..b9d903fb6d 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName3.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName3.yml @@ -16,7 +16,7 @@ initialState: active: {line: 0, character: 0} marks: {} finalState: - documentContents: (aaa) @ + documentContents: "(aaa) " selections: - - anchor: {line: 0, character: 7} - active: {line: 0, character: 7} + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName4.yml index 5965b2f56f..027eced85e 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName4.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName4.yml @@ -16,7 +16,7 @@ initialState: active: {line: 0, character: 0} marks: {} finalState: - documentContents: "\"aaa\" @" + documentContents: "\"aaa\" " selections: - - anchor: {line: 0, character: 7} - active: {line: 0, character: 7} + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName5.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName5.yml index 2f239de12b..61cf415eb4 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName5.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName5.yml @@ -16,7 +16,7 @@ initialState: active: {line: 0, character: 0} marks: {} finalState: - documentContents: "[(aaa) (bbb)] @" + documentContents: "[(aaa) (bbb)] " selections: - - anchor: {line: 0, character: 15} - active: {line: 0, character: 15} + - anchor: {line: 0, character: 14} + active: {line: 0, character: 14} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName6.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName6.yml index 47fc49c9ad..4c0da03687 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName6.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName6.yml @@ -23,8 +23,8 @@ finalState: documentContents: |- (anonymous_node - name: (_) @ + name: (_) ) selections: - - anchor: {line: 2, character: 13} - active: {line: 2, character: 13} + - anchor: {line: 2, character: 12} + active: {line: 2, character: 12} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName7.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName7.yml index 7f29ed6af7..e0b700e0db 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName7.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/clearName7.yml @@ -21,8 +21,8 @@ initialState: finalState: documentContents: |- (anonymous_node - name: (_) @ + name: (_) ) selections: - - anchor: {line: 1, character: 13} - active: {line: 1, character: 13} + - anchor: {line: 1, character: 12} + active: {line: 1, character: 12} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/drinkName.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/drinkName.yml index f4501138fc..7116c6e725 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/drinkName.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/drinkName.yml @@ -17,7 +17,7 @@ initialState: active: {line: 0, character: 0} marks: {} finalState: - documentContents: "eee: _ @ @bbb @ccc" + documentContents: "eee: _ @bbb @ccc" selections: - - anchor: {line: 0, character: 8} - active: {line: 0, character: 8} + - anchor: {line: 0, character: 7} + active: {line: 0, character: 7} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/pourName.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/pourName.yml index 61df78276b..13901f9110 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/pourName.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/pourName.yml @@ -17,7 +17,7 @@ initialState: active: {line: 0, character: 0} marks: {} finalState: - documentContents: "eee: _ @bbb @ccc @" + documentContents: "eee: _ @bbb @ccc " selections: - - anchor: {line: 0, character: 18} - active: {line: 0, character: 18} + - anchor: {line: 0, character: 17} + active: {line: 0, character: 17} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/pourName2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/pourName2.yml index dccd953997..607c1ecb5e 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/pourName2.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/pourName2.yml @@ -17,7 +17,7 @@ initialState: active: {line: 0, character: 7} marks: {} finalState: - documentContents: "eee: _ @bbb @ @ccc" + documentContents: "eee: _ @bbb @ccc" selections: - - anchor: {line: 0, character: 13} - active: {line: 0, character: 13} + - anchor: {line: 0, character: 12} + active: {line: 0, character: 12} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/takeName.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/takeName.yml index 6c7c8a477d..6b089e2645 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/takeName.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/scm/takeName.yml @@ -19,5 +19,5 @@ initialState: finalState: documentContents: (aaa) @bbb @ccc @ddd selections: - - anchor: {line: 0, character: 7} + - anchor: {line: 0, character: 6} active: {line: 0, character: 20} diff --git a/queries/scm.name.scm b/queries/scm.name.scm index 01bd7cc390..1926a67a76 100644 --- a/queries/scm.name.scm +++ b/queries/scm.name.scm @@ -1,59 +1,43 @@ ;;!! (aaa) @bbb @ccc -;;! ^^^^^^^^ -;;! xxxxxxxxx +;;! ^^^^^^^^^ ;;! --------------- ( (_ _ @dummy . - (capture - "@" @_.leading - name: (identifier) @name.start - ) + (capture) @name.start (capture)? @name.end . ) @_.domain (#not-type? @_.domain parameters) (#not-type? @dummy capture) (#not-parent-type? @_.domain field_definition) - (#insertion-delimiter! @name.start " @") ) ;;!! eee: (aaa) @bbb @ccc -;;! ^^^^^^^^ -;;! xxxxxxxxx +;;! ^^^^^^^^^ ;;! -------------------- ( (field_definition (_ _ @dummy . - (capture - "@" @_.leading - name: (identifier) @name.start - ) + (capture) @name.start (capture)? @name.end . ) ) @_.domain (#not-type? @dummy capture) - (#insertion-delimiter! @name.start " @") ) ;;!! (aaa) @bbb @ccc -;;! ^^^ ^^^ -;;! xxxx xxxx -;;! ---- ---- +;;! ^^^^ ^^^^ ( (_ - (capture - "@" @_.leading - name: (identifier) @name - ) @_.domain + (capture) @name ) @dummy (#not-type? @dummy parameters) (#has-multiple-children-of-type? @dummy capture) - (#insertion-delimiter! @name " @") ) ;;!! (aaa) @bbb @ccc From 4afce766c98d4435a44017e0e55c9c7d465f9d9d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 7 Oct 2023 08:24:33 +0200 Subject: [PATCH 53/54] Bump postcss from 8.4.21 to 8.4.31 (#1932) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [postcss](https://github.com/postcss/postcss) from 8.4.21 to 8.4.31.
Release notes

Sourced from postcss's releases.

8.4.31

  • Fixed \r parsing to fix CVE-2023-44270.

8.4.30

8.4.29

8.4.28

  • Fixed Root.source.end for better source map (by @​romainmenke).
  • Fixed Result.root types when process() has no parser.

8.4.27

  • Fixed Container clone methods types.

8.4.26

  • Fixed clone methods types.

8.4.25

8.4.24

  • Fixed Plugin types.

8.4.23

  • Fixed warnings in TypeDoc.

8.4.22

Changelog

Sourced from postcss's changelog.

8.4.31

  • Fixed \r parsing to fix CVE-2023-44270.

8.4.30

  • Improved source map performance (by Romain Menke).

8.4.29

  • Fixed Node#source.offset (by Ido Rosenthal).
  • Fixed docs (by Christian Oliff).

8.4.28

  • Fixed Root.source.end for better source map (by Romain Menke).
  • Fixed Result.root types when process() has no parser.

8.4.27

  • Fixed Container clone methods types.

8.4.26

  • Fixed clone methods types.

8.4.25

8.4.24

  • Fixed Plugin types.

8.4.23

  • Fixed warnings in TypeDoc.

8.4.22

  • Fixed TypeScript support with node16 (by Remco Haszing).
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=postcss&package-manager=npm_and_yarn&previous-version=8.4.21&new-version=8.4.31)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/cursorless-dev/cursorless/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packages/cheatsheet-local/package.json | 2 +- packages/cursorless-org/package.json | 2 +- pnpm-lock.yaml | 358 ++++++++++++------------- 3 files changed, 181 insertions(+), 181 deletions(-) diff --git a/packages/cheatsheet-local/package.json b/packages/cheatsheet-local/package.json index 86095ff96a..e2bb90109d 100644 --- a/packages/cheatsheet-local/package.json +++ b/packages/cheatsheet-local/package.json @@ -41,7 +41,7 @@ "css-loader": "6.7.3", "html-webpack-plugin": "5.5.0", "jest": "29.5.0", - "postcss": "8.4.21", + "postcss": "8.4.31", "postcss-loader": "7.0.2", "style-loader": "3.3.1", "tailwindcss": "3.2.7", diff --git a/packages/cursorless-org/package.json b/packages/cursorless-org/package.json index a2c76201e4..fcffa984f1 100644 --- a/packages/cursorless-org/package.json +++ b/packages/cursorless-org/package.json @@ -29,7 +29,7 @@ "@types/react-dom": "18.0.11", "autoprefixer": "10.4.13", "http-server": "14.1.1", - "postcss": "8.4.21", + "postcss": "8.4.31", "tailwindcss": "3.2.7" }, "license": "MIT", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0aabb70b61..922b29574f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -143,7 +143,7 @@ importers: version: 3.0.7(prettier@3.0.0)(webpack-cli@5.1.4)(webpack@5.88.2) autoprefixer: specifier: 10.4.13 - version: 10.4.13(postcss@8.4.21) + version: 10.4.13(postcss@8.4.31) css-loader: specifier: 6.7.3 version: 6.7.3(webpack@5.88.2) @@ -154,17 +154,17 @@ importers: specifier: 29.5.0 version: 29.5.0(@types/node@16.18.13)(ts-node@10.9.1) postcss: - specifier: 8.4.21 - version: 8.4.21 + specifier: 8.4.31 + version: 8.4.31 postcss-loader: specifier: 7.0.2 - version: 7.0.2(postcss@8.4.21)(webpack@5.88.2) + version: 7.0.2(postcss@8.4.31)(webpack@5.88.2) style-loader: specifier: 3.3.1 version: 3.3.1(webpack@5.88.2) tailwindcss: specifier: 3.2.7 - version: 3.2.7(postcss@8.4.21)(ts-node@10.9.1) + version: 3.2.7(postcss@8.4.31)(ts-node@10.9.1) ts-loader: specifier: 9.4.2 version: 9.4.2(typescript@5.1.6)(webpack@5.88.2) @@ -311,16 +311,16 @@ importers: version: 18.0.11 autoprefixer: specifier: 10.4.13 - version: 10.4.13(postcss@8.4.21) + version: 10.4.13(postcss@8.4.31) http-server: specifier: 14.1.1 version: 14.1.1 postcss: - specifier: 8.4.21 - version: 8.4.21 + specifier: 8.4.31 + version: 8.4.31 tailwindcss: specifier: 3.2.7 - version: 3.2.7(postcss@8.4.21)(ts-node@10.9.1) + version: 3.2.7(postcss@8.4.31)(ts-node@10.9.1) packages/cursorless-org-docs: dependencies: @@ -1963,7 +1963,7 @@ packages: '@docusaurus/utils-validation': 3.0.0-alpha.0(@docusaurus/types@3.0.0-alpha.0) '@slorber/static-site-generator-webpack-plugin': 4.0.7 '@svgr/webpack': 6.5.1 - autoprefixer: 10.4.13(postcss@8.4.21) + autoprefixer: 10.4.13(postcss@8.4.31) babel-loader: 9.1.3(@babel/core@7.22.10)(webpack@5.88.2) babel-plugin-dynamic-import-node: 2.3.3 boxen: 6.2.1 @@ -1977,7 +1977,7 @@ packages: core-js: 3.29.0 css-loader: 6.7.3(webpack@5.88.2) css-minimizer-webpack-plugin: 4.2.2(clean-css@5.3.2)(webpack@5.88.2) - cssnano: 5.1.15(postcss@8.4.21) + cssnano: 5.1.15(postcss@8.4.31) del: 6.1.1 detect-port: 1.5.1 escape-html: 1.0.3 @@ -1991,8 +1991,8 @@ packages: leven: 3.1.0 lodash: 4.17.21 mini-css-extract-plugin: 2.7.3(webpack@5.88.2) - postcss: 8.4.21 - postcss-loader: 7.0.2(postcss@8.4.21)(webpack@5.88.2) + postcss: 8.4.31 + postcss-loader: 7.0.2(postcss@8.4.31)(webpack@5.88.2) prompts: 2.4.2 react: 18.2.0 react-dev-utils: 12.0.1(eslint@8.38.0)(typescript@5.1.6)(webpack@5.88.2) @@ -2040,9 +2040,9 @@ packages: resolution: {integrity: sha512-W+rCRAAv/AIePyWk62tYUxZBRJojBWwIW0/olhhjoEyI4cyLrfUXE1be8gilu0SoDw6S1WukDCoEf+31+9+ZJg==} engines: {node: '>=16.14'} dependencies: - cssnano-preset-advanced: 5.3.10(postcss@8.4.21) - postcss: 8.4.21 - postcss-sort-media-queries: 4.3.0(postcss@8.4.21) + cssnano-preset-advanced: 5.3.10(postcss@8.4.31) + postcss: 8.4.31 + postcss-sort-media-queries: 4.3.0(postcss@8.4.31) tslib: 2.6.1 dev: false @@ -2482,7 +2482,7 @@ packages: infima: 0.2.0-alpha.43 lodash: 4.17.21 nprogress: 0.2.0 - postcss: 8.4.21 + postcss: 8.4.31 prism-react-renderer: 1.3.5(react@18.2.0) prismjs: 1.29.0 react: 18.2.0 @@ -5689,7 +5689,7 @@ packages: engines: {node: '>= 4.0.0'} dev: false - /autoprefixer@10.4.13(postcss@8.4.21): + /autoprefixer@10.4.13(postcss@8.4.31): resolution: {integrity: sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==} engines: {node: ^10 || ^12 || >=14} hasBin: true @@ -5701,7 +5701,7 @@ packages: fraction.js: 4.2.0 normalize-range: 0.1.2 picocolors: 1.0.0 - postcss: 8.4.21 + postcss: 8.4.31 postcss-value-parser: 4.2.0 /available-typed-arrays@1.0.5: @@ -6774,13 +6774,13 @@ packages: type-fest: 1.4.0 dev: false - /css-declaration-sorter@6.3.1(postcss@8.4.21): + /css-declaration-sorter@6.3.1(postcss@8.4.31): resolution: {integrity: sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==} engines: {node: ^10 || ^12 || >=14} peerDependencies: postcss: ^8.0.9 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 dev: false /css-in-js-utils@3.1.0: @@ -6795,12 +6795,12 @@ packages: peerDependencies: webpack: ^5.0.0 dependencies: - icss-utils: 5.1.0(postcss@8.4.21) - postcss: 8.4.21 - postcss-modules-extract-imports: 3.0.0(postcss@8.4.21) - postcss-modules-local-by-default: 4.0.0(postcss@8.4.21) - postcss-modules-scope: 3.0.0(postcss@8.4.21) - postcss-modules-values: 4.0.0(postcss@8.4.21) + icss-utils: 5.1.0(postcss@8.4.31) + postcss: 8.4.31 + postcss-modules-extract-imports: 3.0.0(postcss@8.4.31) + postcss-modules-local-by-default: 4.0.0(postcss@8.4.31) + postcss-modules-scope: 3.0.0(postcss@8.4.31) + postcss-modules-values: 4.0.0(postcss@8.4.31) postcss-value-parser: 4.2.0 semver: 7.5.4 webpack: 5.88.2(webpack-cli@5.1.4) @@ -6831,9 +6831,9 @@ packages: optional: true dependencies: clean-css: 5.3.2 - cssnano: 5.1.15(postcss@8.4.21) + cssnano: 5.1.15(postcss@8.4.31) jest-worker: 29.5.0 - postcss: 8.4.21 + postcss: 8.4.31 schema-utils: 4.0.0 serialize-javascript: 6.0.1 source-map: 0.6.1 @@ -6875,77 +6875,77 @@ packages: engines: {node: '>=4'} hasBin: true - /cssnano-preset-advanced@5.3.10(postcss@8.4.21): + /cssnano-preset-advanced@5.3.10(postcss@8.4.31): resolution: {integrity: sha512-fnYJyCS9jgMU+cmHO1rPSPf9axbQyD7iUhLO5Df6O4G+fKIOMps+ZbU0PdGFejFBBZ3Pftf18fn1eG7MAPUSWQ==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - autoprefixer: 10.4.13(postcss@8.4.21) - cssnano-preset-default: 5.2.14(postcss@8.4.21) - postcss: 8.4.21 - postcss-discard-unused: 5.1.0(postcss@8.4.21) - postcss-merge-idents: 5.1.1(postcss@8.4.21) - postcss-reduce-idents: 5.2.0(postcss@8.4.21) - postcss-zindex: 5.1.0(postcss@8.4.21) + autoprefixer: 10.4.13(postcss@8.4.31) + cssnano-preset-default: 5.2.14(postcss@8.4.31) + postcss: 8.4.31 + postcss-discard-unused: 5.1.0(postcss@8.4.31) + postcss-merge-idents: 5.1.1(postcss@8.4.31) + postcss-reduce-idents: 5.2.0(postcss@8.4.31) + postcss-zindex: 5.1.0(postcss@8.4.31) dev: false - /cssnano-preset-default@5.2.14(postcss@8.4.21): + /cssnano-preset-default@5.2.14(postcss@8.4.31): resolution: {integrity: sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - css-declaration-sorter: 6.3.1(postcss@8.4.21) - cssnano-utils: 3.1.0(postcss@8.4.21) - postcss: 8.4.21 - postcss-calc: 8.2.4(postcss@8.4.21) - postcss-colormin: 5.3.1(postcss@8.4.21) - postcss-convert-values: 5.1.3(postcss@8.4.21) - postcss-discard-comments: 5.1.2(postcss@8.4.21) - postcss-discard-duplicates: 5.1.0(postcss@8.4.21) - postcss-discard-empty: 5.1.1(postcss@8.4.21) - postcss-discard-overridden: 5.1.0(postcss@8.4.21) - postcss-merge-longhand: 5.1.7(postcss@8.4.21) - postcss-merge-rules: 5.1.4(postcss@8.4.21) - postcss-minify-font-values: 5.1.0(postcss@8.4.21) - postcss-minify-gradients: 5.1.1(postcss@8.4.21) - postcss-minify-params: 5.1.4(postcss@8.4.21) - postcss-minify-selectors: 5.2.1(postcss@8.4.21) - postcss-normalize-charset: 5.1.0(postcss@8.4.21) - postcss-normalize-display-values: 5.1.0(postcss@8.4.21) - postcss-normalize-positions: 5.1.1(postcss@8.4.21) - postcss-normalize-repeat-style: 5.1.1(postcss@8.4.21) - postcss-normalize-string: 5.1.0(postcss@8.4.21) - postcss-normalize-timing-functions: 5.1.0(postcss@8.4.21) - postcss-normalize-unicode: 5.1.1(postcss@8.4.21) - postcss-normalize-url: 5.1.0(postcss@8.4.21) - postcss-normalize-whitespace: 5.1.1(postcss@8.4.21) - postcss-ordered-values: 5.1.3(postcss@8.4.21) - postcss-reduce-initial: 5.1.2(postcss@8.4.21) - postcss-reduce-transforms: 5.1.0(postcss@8.4.21) - postcss-svgo: 5.1.0(postcss@8.4.21) - postcss-unique-selectors: 5.1.1(postcss@8.4.21) - dev: false - - /cssnano-utils@3.1.0(postcss@8.4.21): + css-declaration-sorter: 6.3.1(postcss@8.4.31) + cssnano-utils: 3.1.0(postcss@8.4.31) + postcss: 8.4.31 + postcss-calc: 8.2.4(postcss@8.4.31) + postcss-colormin: 5.3.1(postcss@8.4.31) + postcss-convert-values: 5.1.3(postcss@8.4.31) + postcss-discard-comments: 5.1.2(postcss@8.4.31) + postcss-discard-duplicates: 5.1.0(postcss@8.4.31) + postcss-discard-empty: 5.1.1(postcss@8.4.31) + postcss-discard-overridden: 5.1.0(postcss@8.4.31) + postcss-merge-longhand: 5.1.7(postcss@8.4.31) + postcss-merge-rules: 5.1.4(postcss@8.4.31) + postcss-minify-font-values: 5.1.0(postcss@8.4.31) + postcss-minify-gradients: 5.1.1(postcss@8.4.31) + postcss-minify-params: 5.1.4(postcss@8.4.31) + postcss-minify-selectors: 5.2.1(postcss@8.4.31) + postcss-normalize-charset: 5.1.0(postcss@8.4.31) + postcss-normalize-display-values: 5.1.0(postcss@8.4.31) + postcss-normalize-positions: 5.1.1(postcss@8.4.31) + postcss-normalize-repeat-style: 5.1.1(postcss@8.4.31) + postcss-normalize-string: 5.1.0(postcss@8.4.31) + postcss-normalize-timing-functions: 5.1.0(postcss@8.4.31) + postcss-normalize-unicode: 5.1.1(postcss@8.4.31) + postcss-normalize-url: 5.1.0(postcss@8.4.31) + postcss-normalize-whitespace: 5.1.1(postcss@8.4.31) + postcss-ordered-values: 5.1.3(postcss@8.4.31) + postcss-reduce-initial: 5.1.2(postcss@8.4.31) + postcss-reduce-transforms: 5.1.0(postcss@8.4.31) + postcss-svgo: 5.1.0(postcss@8.4.31) + postcss-unique-selectors: 5.1.1(postcss@8.4.31) + dev: false + + /cssnano-utils@3.1.0(postcss@8.4.31): resolution: {integrity: sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 dev: false - /cssnano@5.1.15(postcss@8.4.21): + /cssnano@5.1.15(postcss@8.4.31): resolution: {integrity: sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - cssnano-preset-default: 5.2.14(postcss@8.4.21) + cssnano-preset-default: 5.2.14(postcss@8.4.31) lilconfig: 2.1.0 - postcss: 8.4.21 + postcss: 8.4.31 yaml: 1.10.2 dev: false @@ -9144,13 +9144,13 @@ packages: dependencies: safer-buffer: 2.1.2 - /icss-utils@5.1.0(postcss@8.4.21): + /icss-utils@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 /ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -12484,17 +12484,17 @@ packages: - supports-color dev: true - /postcss-calc@8.2.4(postcss@8.4.21): + /postcss-calc@8.2.4(postcss@8.4.31): resolution: {integrity: sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==} peerDependencies: postcss: ^8.2.2 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 postcss-selector-parser: 6.0.11 postcss-value-parser: 4.2.0 dev: false - /postcss-colormin@5.3.1(postcss@8.4.21): + /postcss-colormin@5.3.1(postcss@8.4.31): resolution: {integrity: sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: @@ -12503,90 +12503,90 @@ packages: browserslist: 4.21.10 caniuse-api: 3.0.0 colord: 2.9.3 - postcss: 8.4.21 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-convert-values@5.1.3(postcss@8.4.21): + /postcss-convert-values@5.1.3(postcss@8.4.31): resolution: {integrity: sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: browserslist: 4.21.10 - postcss: 8.4.21 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-discard-comments@5.1.2(postcss@8.4.21): + /postcss-discard-comments@5.1.2(postcss@8.4.31): resolution: {integrity: sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 dev: false - /postcss-discard-duplicates@5.1.0(postcss@8.4.21): + /postcss-discard-duplicates@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 dev: false - /postcss-discard-empty@5.1.1(postcss@8.4.21): + /postcss-discard-empty@5.1.1(postcss@8.4.31): resolution: {integrity: sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 dev: false - /postcss-discard-overridden@5.1.0(postcss@8.4.21): + /postcss-discard-overridden@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 dev: false - /postcss-discard-unused@5.1.0(postcss@8.4.21): + /postcss-discard-unused@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-KwLWymI9hbwXmJa0dkrzpRbSJEh0vVUd7r8t0yOGPcfKzyJJxFM8kLyC5Ev9avji6nY95pOp1W6HqIrfT+0VGw==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 postcss-selector-parser: 6.0.11 dev: false - /postcss-import@14.1.0(postcss@8.4.21): + /postcss-import@14.1.0(postcss@8.4.31): resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==} engines: {node: '>=10.0.0'} peerDependencies: postcss: ^8.0.0 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.4 dev: true - /postcss-js@4.0.1(postcss@8.4.21): + /postcss-js@4.0.1(postcss@8.4.31): resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} engines: {node: ^12 || ^14 || >= 16} peerDependencies: postcss: ^8.4.21 dependencies: camelcase-css: 2.0.1 - postcss: 8.4.21 + postcss: 8.4.31 dev: true - /postcss-load-config@3.1.4(postcss@8.4.21)(ts-node@10.9.1): + /postcss-load-config@3.1.4(postcss@8.4.31)(ts-node@10.9.1): resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} engines: {node: '>= 10'} peerDependencies: @@ -12599,12 +12599,12 @@ packages: optional: true dependencies: lilconfig: 2.1.0 - postcss: 8.4.21 + postcss: 8.4.31 ts-node: 10.9.1(@types/node@16.18.13)(typescript@5.1.6) yaml: 1.10.2 dev: true - /postcss-loader@7.0.2(postcss@8.4.21)(webpack@5.88.2): + /postcss-loader@7.0.2(postcss@8.4.31)(webpack@5.88.2): resolution: {integrity: sha512-fUJzV/QH7NXUAqV8dWJ9Lg4aTkDCezpTS5HgJ2DvqznexTbSTxgi/dTECvTZ15BwKTtk8G/bqI/QTu2HPd3ZCg==} engines: {node: '>= 14.15.0'} peerDependencies: @@ -12613,33 +12613,33 @@ packages: dependencies: cosmiconfig: 7.1.0 klona: 2.0.6 - postcss: 8.4.21 + postcss: 8.4.31 semver: 7.5.4 webpack: 5.88.2(webpack-cli@5.1.4) - /postcss-merge-idents@5.1.1(postcss@8.4.21): + /postcss-merge-idents@5.1.1(postcss@8.4.31): resolution: {integrity: sha512-pCijL1TREiCoog5nQp7wUe+TUonA2tC2sQ54UGeMmryK3UFGIYKqDyjnqd6RcuI4znFn9hWSLNN8xKE/vWcUQw==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - cssnano-utils: 3.1.0(postcss@8.4.21) - postcss: 8.4.21 + cssnano-utils: 3.1.0(postcss@8.4.31) + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-merge-longhand@5.1.7(postcss@8.4.21): + /postcss-merge-longhand@5.1.7(postcss@8.4.31): resolution: {integrity: sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 postcss-value-parser: 4.2.0 - stylehacks: 5.1.1(postcss@8.4.21) + stylehacks: 5.1.1(postcss@8.4.31) dev: false - /postcss-merge-rules@5.1.4(postcss@8.4.21): + /postcss-merge-rules@5.1.4(postcss@8.4.31): resolution: {integrity: sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: @@ -12647,215 +12647,215 @@ packages: dependencies: browserslist: 4.21.10 caniuse-api: 3.0.0 - cssnano-utils: 3.1.0(postcss@8.4.21) - postcss: 8.4.21 + cssnano-utils: 3.1.0(postcss@8.4.31) + postcss: 8.4.31 postcss-selector-parser: 6.0.11 dev: false - /postcss-minify-font-values@5.1.0(postcss@8.4.21): + /postcss-minify-font-values@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-minify-gradients@5.1.1(postcss@8.4.21): + /postcss-minify-gradients@5.1.1(postcss@8.4.31): resolution: {integrity: sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: colord: 2.9.3 - cssnano-utils: 3.1.0(postcss@8.4.21) - postcss: 8.4.21 + cssnano-utils: 3.1.0(postcss@8.4.31) + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-minify-params@5.1.4(postcss@8.4.21): + /postcss-minify-params@5.1.4(postcss@8.4.31): resolution: {integrity: sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: browserslist: 4.21.10 - cssnano-utils: 3.1.0(postcss@8.4.21) - postcss: 8.4.21 + cssnano-utils: 3.1.0(postcss@8.4.31) + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-minify-selectors@5.2.1(postcss@8.4.21): + /postcss-minify-selectors@5.2.1(postcss@8.4.31): resolution: {integrity: sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 postcss-selector-parser: 6.0.11 dev: false - /postcss-modules-extract-imports@3.0.0(postcss@8.4.21): + /postcss-modules-extract-imports@3.0.0(postcss@8.4.31): resolution: {integrity: sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 - /postcss-modules-local-by-default@4.0.0(postcss@8.4.21): + /postcss-modules-local-by-default@4.0.0(postcss@8.4.31): resolution: {integrity: sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - icss-utils: 5.1.0(postcss@8.4.21) - postcss: 8.4.21 + icss-utils: 5.1.0(postcss@8.4.31) + postcss: 8.4.31 postcss-selector-parser: 6.0.11 postcss-value-parser: 4.2.0 - /postcss-modules-scope@3.0.0(postcss@8.4.21): + /postcss-modules-scope@3.0.0(postcss@8.4.31): resolution: {integrity: sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 postcss-selector-parser: 6.0.11 - /postcss-modules-values@4.0.0(postcss@8.4.21): + /postcss-modules-values@4.0.0(postcss@8.4.31): resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - icss-utils: 5.1.0(postcss@8.4.21) - postcss: 8.4.21 + icss-utils: 5.1.0(postcss@8.4.31) + postcss: 8.4.31 - /postcss-nested@6.0.0(postcss@8.4.21): + /postcss-nested@6.0.0(postcss@8.4.31): resolution: {integrity: sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==} engines: {node: '>=12.0'} peerDependencies: postcss: ^8.2.14 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 postcss-selector-parser: 6.0.11 dev: true - /postcss-normalize-charset@5.1.0(postcss@8.4.21): + /postcss-normalize-charset@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 dev: false - /postcss-normalize-display-values@5.1.0(postcss@8.4.21): + /postcss-normalize-display-values@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-positions@5.1.1(postcss@8.4.21): + /postcss-normalize-positions@5.1.1(postcss@8.4.31): resolution: {integrity: sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-repeat-style@5.1.1(postcss@8.4.21): + /postcss-normalize-repeat-style@5.1.1(postcss@8.4.31): resolution: {integrity: sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-string@5.1.0(postcss@8.4.21): + /postcss-normalize-string@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-timing-functions@5.1.0(postcss@8.4.21): + /postcss-normalize-timing-functions@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-unicode@5.1.1(postcss@8.4.21): + /postcss-normalize-unicode@5.1.1(postcss@8.4.31): resolution: {integrity: sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: browserslist: 4.21.10 - postcss: 8.4.21 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-url@5.1.0(postcss@8.4.21): + /postcss-normalize-url@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: normalize-url: 6.1.0 - postcss: 8.4.21 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-whitespace@5.1.1(postcss@8.4.21): + /postcss-normalize-whitespace@5.1.1(postcss@8.4.31): resolution: {integrity: sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-ordered-values@5.1.3(postcss@8.4.21): + /postcss-ordered-values@5.1.3(postcss@8.4.31): resolution: {integrity: sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - cssnano-utils: 3.1.0(postcss@8.4.21) - postcss: 8.4.21 + cssnano-utils: 3.1.0(postcss@8.4.31) + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-reduce-idents@5.2.0(postcss@8.4.21): + /postcss-reduce-idents@5.2.0(postcss@8.4.31): resolution: {integrity: sha512-BTrLjICoSB6gxbc58D5mdBK8OhXRDqud/zodYfdSi52qvDHdMwk+9kB9xsM8yJThH/sZU5A6QVSmMmaN001gIg==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-reduce-initial@5.1.2(postcss@8.4.21): + /postcss-reduce-initial@5.1.2(postcss@8.4.31): resolution: {integrity: sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: @@ -12863,16 +12863,16 @@ packages: dependencies: browserslist: 4.21.10 caniuse-api: 3.0.0 - postcss: 8.4.21 + postcss: 8.4.31 dev: false - /postcss-reduce-transforms@5.1.0(postcss@8.4.21): + /postcss-reduce-transforms@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false @@ -12883,47 +12883,47 @@ packages: cssesc: 3.0.0 util-deprecate: 1.0.2 - /postcss-sort-media-queries@4.3.0(postcss@8.4.21): + /postcss-sort-media-queries@4.3.0(postcss@8.4.31): resolution: {integrity: sha512-jAl8gJM2DvuIJiI9sL1CuiHtKM4s5aEIomkU8G3LFvbP+p8i7Sz8VV63uieTgoewGqKbi+hxBTiOKJlB35upCg==} engines: {node: '>=10.0.0'} peerDependencies: postcss: ^8.4.16 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 sort-css-media-queries: 2.1.0 dev: false - /postcss-svgo@5.1.0(postcss@8.4.21): + /postcss-svgo@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 postcss-value-parser: 4.2.0 svgo: 2.8.0 dev: false - /postcss-unique-selectors@5.1.1(postcss@8.4.21): + /postcss-unique-selectors@5.1.1(postcss@8.4.31): resolution: {integrity: sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 postcss-selector-parser: 6.0.11 dev: false /postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - /postcss-zindex@5.1.0(postcss@8.4.21): + /postcss-zindex@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-fgFMf0OtVSBR1va1JNHYgMxYk73yhn/qb4uQDq1DLGYolz8gHCyr/sesEuGUaYs58E3ZJRcpoGuPVoB7Meiq9A==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.21 + postcss: 8.4.31 dev: false /postcss@8.4.14: @@ -12935,8 +12935,8 @@ packages: source-map-js: 1.0.2 dev: false - /postcss@8.4.21: - resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==} + /postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} dependencies: nanoid: 3.3.6 @@ -13865,7 +13865,7 @@ packages: dependencies: escalade: 3.1.1 picocolors: 1.0.0 - postcss: 8.4.21 + postcss: 8.4.31 strip-json-comments: 3.1.1 dev: false @@ -13932,7 +13932,7 @@ packages: htmlparser2: 8.0.1 is-plain-object: 5.0.0 parse-srcset: 1.0.2 - postcss: 8.4.21 + postcss: 8.4.31 dev: false /sax@1.2.4: @@ -14654,14 +14654,14 @@ packages: react: 18.2.0 dev: false - /stylehacks@5.1.1(postcss@8.4.21): + /stylehacks@5.1.1(postcss@8.4.31): resolution: {integrity: sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: browserslist: 4.21.10 - postcss: 8.4.21 + postcss: 8.4.31 postcss-selector-parser: 6.0.11 dev: false @@ -14736,7 +14736,7 @@ packages: zod: 3.20.6 dev: true - /tailwindcss@3.2.7(postcss@8.4.21)(ts-node@10.9.1): + /tailwindcss@3.2.7(postcss@8.4.31)(ts-node@10.9.1): resolution: {integrity: sha512-B6DLqJzc21x7wntlH/GsZwEXTBttVSl1FtCzC8WP4oBc/NKef7kaax5jeihkkCEWc831/5NDJ9gRNDK6NEioQQ==} engines: {node: '>=12.13.0'} hasBin: true @@ -14757,11 +14757,11 @@ packages: normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.0.0 - postcss: 8.4.21 - postcss-import: 14.1.0(postcss@8.4.21) - postcss-js: 4.0.1(postcss@8.4.21) - postcss-load-config: 3.1.4(postcss@8.4.21)(ts-node@10.9.1) - postcss-nested: 6.0.0(postcss@8.4.21) + postcss: 8.4.31 + postcss-import: 14.1.0(postcss@8.4.31) + postcss-js: 4.0.1(postcss@8.4.31) + postcss-load-config: 3.1.4(postcss@8.4.31)(ts-node@10.9.1) + postcss-nested: 6.0.0(postcss@8.4.31) postcss-selector-parser: 6.0.11 postcss-value-parser: 4.2.0 quick-lru: 5.1.1 From 054b1933847317c17e4f36ad4700e818fbcce01f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 8 Oct 2023 17:00:08 +0000 Subject: [PATCH 54/54] Bump zod from 3.21.4 to 3.22.3 in /packages/cursorless-engine (#1931) Bumps [zod](https://github.com/colinhacks/zod) from 3.21.4 to 3.22.3.
Release notes

Sourced from zod's releases.

v3.22.3

Commits:

  • 1e23990bcdd33d1e81b31e40e77a031fcfd87ce1 Commit
  • 9bd3879b482f139fd03d5025813ee66a04195cdd docs: remove obsolete text about readonly types (#2676)
  • f59be093ec21430d9f32bbcb628d7e39116adf34 clarify datetime ISO 8601 (#2673)
  • 64dcc8e2b16febe48fa8e3c82c47c92643e6c9e3 Update sponsors
  • 18115a8f128680b4526df58ce96deab7dce93b93 Formatting
  • 28c19273658b164c53c149785fa7a8187c428ad4 Update sponsors
  • ad2ee9ccf723c4388158ff6b8669c2a6cdc85643 2718 Updated Custom Schemas documentation example to use type narrowing (#2778)
  • ae0f7a2c15e7741ee1b23c03a3bfb9acebd86551 docs: update ref to discriminated-unions docs (#2485)
  • 2ba00fe2377f4d53947a84b8cdb314a63bbd6dd4 [2609] fix ReDoS vulnerability in email regex (#2824)
  • 1e61d76cdec05de9271fc0df58798ddf9ce94923 3.22.3

v3.22.2

Commits:

  • 13d9e6bda286cbd4c1b177171273695d8309e5de Fix lint
  • 0d49f10b3c25a8e4cbb6534cc0773b195c56d06d docs: add typeschema to ecosystem (#2626)
  • 8e4af7b56df6f2e3daf0dd825b986f1d963025ce X to Zod: add app.quicktype.io (#2668)
  • 792b3ef0d41c144cd10641c6966b98dae1222d82 Fix superrefine types

v3.22.1

Commits:

Fix handing of this in ZodFunction schemas. The parse logic for function schemas now requires the Reflect API.

const methodObject = z.object({
  property: z.number(),
  method: z.function().args(z.string()).returns(z.number()),
});
const methodInstance = {
  property: 3,
  method: function (s: string) {
    return s.length + this.property;
  },
};
const parsed = methodObject.parse(methodInstance);
parsed.method("length=8"); // => 11 (8 length + 3 property)
  • 932cc472d2e66430d368a409b8d251909d7d8d21 Initial prototype fix for issue #2651 (#2652)
  • 0a055e726ac210ef6efc69aa70cd2491767f6060 3.22.1

v3.22.0

ZodReadonly

This release introduces ZodReadonly and the .readonly() method on ZodType.

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=zod&package-manager=npm_and_yarn&previous-version=3.21.4&new-version=3.22.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/cursorless-dev/cursorless/network/alerts).
--------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Pokey Rule <755842+pokey@users.noreply.github.com> --- packages/cursorless-engine/package.json | 2 +- .../TreeSitterQuery/PredicateOperatorSchemaTypes.ts | 2 +- .../languages/TreeSitterQuery/QueryPredicateOperator.ts | 2 +- .../TreeSitterQuery/constructZodErrorMessages.ts | 2 +- .../TreeSitterQuery/operatorArgumentSchemaTypes.ts | 2 +- .../languages/TreeSitterQuery/queryPredicateOperators.ts | 2 +- pnpm-lock.yaml | 8 ++++---- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/cursorless-engine/package.json b/packages/cursorless-engine/package.json index 27cd57db23..34054c00af 100644 --- a/packages/cursorless-engine/package.json +++ b/packages/cursorless-engine/package.json @@ -19,7 +19,7 @@ "lodash": "^4.17.21", "node-html-parser": "^5.3.3", "sbd": "^1.0.19", - "zod": "3.21.4" + "zod": "3.22.3" }, "devDependencies": { "@types/js-yaml": "^4.0.2", diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/PredicateOperatorSchemaTypes.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/PredicateOperatorSchemaTypes.ts index 5d76b6bea7..9805367121 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/PredicateOperatorSchemaTypes.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/PredicateOperatorSchemaTypes.ts @@ -1,4 +1,4 @@ -import z from "zod"; +import { z } from "zod"; import { SchemaInputType, SchemaOutputType, diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/QueryPredicateOperator.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/QueryPredicateOperator.ts index 61cdb80318..aa2436a170 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/QueryPredicateOperator.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/QueryPredicateOperator.ts @@ -1,5 +1,5 @@ import { PredicateOperand } from "web-tree-sitter"; -import z from "zod"; +import { z } from "zod"; import { AcceptFunctionArgs, HasSchema, diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/constructZodErrorMessages.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/constructZodErrorMessages.ts index 41768b74ce..f2889b3d13 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/constructZodErrorMessages.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/constructZodErrorMessages.ts @@ -1,5 +1,5 @@ import { PredicateOperand } from "web-tree-sitter"; -import z from "zod"; +import { z } from "zod"; import { operandToString } from "./predicateToString"; export function constructZodErrorMessages( diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/operatorArgumentSchemaTypes.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/operatorArgumentSchemaTypes.ts index db6a6eadd4..bd068d453e 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/operatorArgumentSchemaTypes.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/operatorArgumentSchemaTypes.ts @@ -1,4 +1,4 @@ -import z from "zod"; +import { z } from "zod"; import { assertTypesEqual } from "./assertTypesEqual"; import { PredicateOperand } from "web-tree-sitter"; diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts index 6ac7210b79..2218c937df 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts @@ -1,5 +1,5 @@ import { Range } from "@cursorless/common"; -import z from "zod"; +import { z } from "zod"; import { makeRangeFromPositions } from "../../util/nodeSelectors"; import { MutableQueryCapture } from "./QueryCapture"; import { QueryPredicateOperator } from "./QueryPredicateOperator"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 922b29574f..b70d24e54e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -239,8 +239,8 @@ importers: specifier: ^1.0.19 version: 1.0.19 zod: - specifier: 3.21.4 - version: 3.21.4 + specifier: 3.22.3 + version: 3.22.3 devDependencies: '@types/js-yaml': specifier: ^4.0.2 @@ -16195,8 +16195,8 @@ packages: resolution: {integrity: sha512-oyu0m54SGCtzh6EClBVqDDlAYRz4jrVtKwQ7ZnsEmMI9HnzuZFj8QFwAY1M5uniIYACdGvv0PBWPF2kO0aNofA==} dev: true - /zod@3.21.4: - resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==} + /zod@3.22.3: + resolution: {integrity: sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==} dev: false /zwitch@2.0.4: