diff --git a/cursorless-talon/src/apps/vscode_settings.py b/cursorless-talon/src/apps/vscode_settings.py index 20e1e306ec..9caa61c4ce 100644 --- a/cursorless-talon/src/apps/vscode_settings.py +++ b/cursorless-talon/src/apps/vscode_settings.py @@ -99,6 +99,7 @@ def vscode_settings_path() -> Path: xdg_config_home / "Code/User/settings.json", xdg_config_home / "VSCodium/User/settings.json", xdg_config_home / "Code - OSS/User/settings.json", + xdg_config_home / "Cursor/User/settings.json", flatpak_apps / "com.visualstudio.code/config/Code/User/settings.json", flatpak_apps / "com.vscodium.codium/config/VSCodium/User/settings.json", flatpak_apps diff --git a/cursorless-talon/src/modifiers/ordinal_scope.py b/cursorless-talon/src/modifiers/ordinal_scope.py index 0ff0ac3982..db617c59eb 100644 --- a/cursorless-talon/src/modifiers/ordinal_scope.py +++ b/cursorless-talon/src/modifiers/ordinal_scope.py @@ -13,7 +13,7 @@ @mod.capture( rule=" | [] {user.cursorless_last_modifier}" ) -def ordinal_or_last(m) -> int: +def cursorless_ordinal_or_last(m) -> int: """An ordinal or the word 'last'""" if m[-1] == "last": return -getattr(m, "ordinals_small", 1) @@ -21,16 +21,16 @@ def ordinal_or_last(m) -> int: @mod.capture( - rule=" [ ] " + rule=" [ ] " ) def cursorless_ordinal_range(m) -> dict[str, Any]: """Ordinal range""" anchor = create_ordinal_scope_modifier( - m.cursorless_scope_type, m.ordinal_or_last_list[0] + m.cursorless_scope_type, m.cursorless_ordinal_or_last_list[0] ) - if len(m.ordinal_or_last_list) > 1: + if len(m.cursorless_ordinal_or_last_list) > 1: active = create_ordinal_scope_modifier( - m.cursorless_scope_type, m.ordinal_or_last_list[1] + m.cursorless_scope_type, m.cursorless_ordinal_or_last_list[1] ) range_connective: RangeConnective = m.cursorless_range_connective return { diff --git a/cursorless-talon/src/modifiers/simple_scope_modifier.py b/cursorless-talon/src/modifiers/simple_scope_modifier.py index a6c40e6797..46f04ff930 100644 --- a/cursorless-talon/src/modifiers/simple_scope_modifier.py +++ b/cursorless-talon/src/modifiers/simple_scope_modifier.py @@ -16,7 +16,8 @@ # This is a private setting and should not be used by non Cursorless developers mod.setting( "private_cursorless_use_preferred_scope", - bool, + type=bool, + default=False, desc="Use preferred scope instead of containing scope for all scopes by default (EXPERIMENTAL)", ) @@ -42,7 +43,7 @@ def cursorless_simple_scope_modifier(m) -> dict[str, Any]: "ancestorIndex": 1, } - if settings.get("user.private_cursorless_use_preferred_scope", False): + if settings.get("user.private_cursorless_use_preferred_scope"): return { "type": "preferredScope", "scopeType": m.cursorless_scope_type, diff --git a/data/fixtures/recorded/languages/typescript/takeItem.yml b/data/fixtures/recorded/languages/typescript/takeItem.yml index 09f6fd5472..d9e43a6ab2 100644 --- a/data/fixtures/recorded/languages/typescript/takeItem.yml +++ b/data/fixtures/recorded/languages/typescript/takeItem.yml @@ -23,5 +23,5 @@ finalState: const value = { a: 1, b: 2, c: 3 }; selections: - - anchor: {line: 1, character: 0} - active: {line: 1, character: 35} + - anchor: {line: 1, character: 6} + active: {line: 1, character: 34} diff --git a/data/fixtures/scopes/javascript.core/collectionItem.unenclosed.scope b/data/fixtures/scopes/javascript.core/collectionItem.unenclosed.scope new file mode 100644 index 0000000000..703a4b32bb --- /dev/null +++ b/data/fixtures/scopes/javascript.core/collectionItem.unenclosed.scope @@ -0,0 +1,33 @@ +let foo, bar; +--- + +[#1 Content] = +[#1 Domain] = 0:4-0:7 + >---< +0| let foo, bar; + +[#1 Removal] = 0:4-0:9 + >-----< +0| let foo, bar; + +[#1 Trailing delimiter] = 0:7-0:9 + >--< +0| let foo, bar; + +[#1 Insertion delimiter] = ", " + + +[#2 Content] = +[#2 Domain] = 0:9-0:12 + >---< +0| let foo, bar; + +[#2 Removal] = 0:7-0:12 + >-----< +0| let foo, bar; + +[#2 Leading delimiter] = 0:7-0:9 + >--< +0| let foo, bar; + +[#2 Insertion delimiter] = ", " diff --git a/data/fixtures/scopes/rust/string.singleLine.scope b/data/fixtures/scopes/rust/string.singleLine.scope new file mode 100644 index 0000000000..68b4966a86 --- /dev/null +++ b/data/fixtures/scopes/rust/string.singleLine.scope @@ -0,0 +1,30 @@ +"aaa" +r#"bbb"# +r##"ccc"## +--- + +[#1 Content] = +[#1 Removal] = +[#1 Domain] = 0:0-0:5 + >-----< +0| "aaa" + +[#1 Insertion delimiter] = " " + + +[#2 Content] = +[#2 Removal] = +[#2 Domain] = 1:0-1:8 + >--------< +1| r#"bbb"# + +[#2 Insertion delimiter] = " " + + +[#3 Content] = +[#3 Removal] = +[#3 Domain] = 2:0-2:10 + >----------< +2| r##"ccc"## + +[#3 Insertion delimiter] = " " diff --git a/data/fixtures/scopes/rust/textFragment.string.singleLine.scope b/data/fixtures/scopes/rust/textFragment.string.singleLine.scope new file mode 100644 index 0000000000..9d115cbc7b --- /dev/null +++ b/data/fixtures/scopes/rust/textFragment.string.singleLine.scope @@ -0,0 +1,30 @@ +"aaa" +r#"bbb"# +r##"ccc"## +--- + +[#1 Content] = +[#1 Removal] = +[#1 Domain] = 0:1-0:4 + >---< +0| "aaa" + +[#1 Insertion delimiter] = " " + + +[#2 Content] = +[#2 Removal] = +[#2 Domain] = 1:3-1:6 + >---< +1| r#"bbb"# + +[#2 Insertion delimiter] = " " + + +[#3 Content] = +[#3 Removal] = +[#3 Domain] = 2:4-2:7 + >---< +2| r##"ccc"## + +[#3 Insertion delimiter] = " " diff --git a/dont_clone_monorepo.py b/dont_clone_monorepo.py new file mode 100644 index 0000000000..b3e120ced1 --- /dev/null +++ b/dont_clone_monorepo.py @@ -0,0 +1,17 @@ +""" +This file exists in our monorepo so that if someone accidentally clones this monorepo +into their talon folder instead of cursorless-talon, they'll get a helpful error popup +""" + +from talon import app + + +def on_ready(): + app.notify( + "Whoops! You cloned our monorepo instead of cursorless-talon", + "Please remove cursorless and clone cursorless-talon instead:", + "https://github.com/cursorless-dev/cursorless-talon", + ) + + +app.register("ready", on_ready) diff --git a/packages/common/package.json b/packages/common/package.json index 5803375623..a072e9f09d 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -23,7 +23,7 @@ "@types/js-yaml": "^4.0.9", "@types/lodash-es": "4.17.12", "@types/mocha": "^10.0.9", - "cross-spawn": "7.0.3", + "cross-spawn": "7.0.5", "fast-check": "3.22.0", "js-yaml": "^4.1.0", "mocha": "^10.7.3" diff --git a/packages/common/src/scopeSupportFacets/javascript.ts b/packages/common/src/scopeSupportFacets/javascript.ts index 232650fd0d..0b44f2c6c3 100644 --- a/packages/common/src/scopeSupportFacets/javascript.ts +++ b/packages/common/src/scopeSupportFacets/javascript.ts @@ -112,6 +112,8 @@ export const javascriptCoreScopeSupport: LanguageScopeSupportFacetMap = { "value.return": supported, "value.return.lambda": supported, "value.field": supported, + + "collectionItem.unenclosed": supported, }; export const javascriptJsxScopeSupport: LanguageScopeSupportFacetMap = { diff --git a/packages/common/src/scopeSupportFacets/rust.ts b/packages/common/src/scopeSupportFacets/rust.ts index 125b6a5dd6..c8e7f844a8 100644 --- a/packages/common/src/scopeSupportFacets/rust.ts +++ b/packages/common/src/scopeSupportFacets/rust.ts @@ -7,4 +7,6 @@ const { supported, unsupported, notApplicable } = ScopeSupportFacetLevel; export const rustScopeSupport: LanguageScopeSupportFacetMap = { ifStatement: supported, disqualifyDelimiter: supported, + "string.singleLine": supported, + "textFragment.string.singleLine": supported, }; diff --git a/packages/cursorless-engine/src/processTargets/modifiers/ContainingScopeStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/ContainingScopeStage.ts index 41e12f4efe..6b22f1c783 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/ContainingScopeStage.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/ContainingScopeStage.ts @@ -45,23 +45,39 @@ export class ContainingScopeStage implements ModifierStage { .run(target); } - const containingScope = getContainingScopeTarget( + const containingScopes = getContainingScopeTarget( target, scopeHandler, ancestorIndex, ); - - if (containingScope == null) { - if (scopeType.type === "collectionItem") { - // For `collectionItem`, fall back to generic implementation - return this.modifierStageFactory + if (scopeType.type === "collectionItem") { + // For `collectionItem`, combine with generic implementation + try { + const legacyScopes = this.modifierStageFactory .getLegacyScopeStage(this.modifier) .run(target); + if (containingScopes == null) { + return legacyScopes; + } + if (containingScopes.length === 1 && legacyScopes.length === 1) { + const containingRange = containingScopes[0].contentRange; + const legacyRange = legacyScopes[0].contentRange; + if ( + containingRange.contains(legacyRange) && + !containingRange.isRangeEqual(legacyRange) + ) { + return legacyScopes; + } + } + } catch (_ex) { + // Do nothing } + } + if (containingScopes == null) { throw new NoContainingScopeError(this.modifier.scopeType.type); } - return containingScope; + return containingScopes; } } diff --git a/packages/test-harness/package.json b/packages/test-harness/package.json index 462f482af5..2e403f2111 100644 --- a/packages/test-harness/package.json +++ b/packages/test-harness/package.json @@ -40,7 +40,7 @@ "@types/mocha": "^10.0.9", "@types/tail": "2.2.3", "@vscode/test-electron": "^2.4.1", - "cross-spawn": "7.0.3", + "cross-spawn": "7.0.5", "mocha": "^10.7.3" }, "types": "./out/runners/extensionTestsNeovim.d.ts", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f26b94e788..623716e576 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -224,8 +224,8 @@ importers: specifier: ^10.0.9 version: 10.0.9 cross-spawn: - specifier: 7.0.3 - version: 7.0.3 + specifier: 7.0.5 + version: 7.0.5 fast-check: specifier: 3.22.0 version: 3.22.0 @@ -979,8 +979,8 @@ importers: specifier: ^2.4.1 version: 2.4.1 cross-spawn: - specifier: 7.0.3 - version: 7.0.3 + specifier: 7.0.5 + version: 7.0.5 mocha: specifier: ^10.7.3 version: 10.7.3 @@ -2121,8 +2121,8 @@ packages: resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.2.0': - resolution: {integrity: sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==} + '@eslint/plugin-kit@0.2.3': + resolution: {integrity: sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@fortawesome/fontawesome-common-types@6.6.0': @@ -4393,8 +4393,8 @@ packages: create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + cross-spawn@7.0.5: + resolution: {integrity: sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==} engines: {node: '>= 8'} crypto-random-string@2.0.0: @@ -11919,7 +11919,7 @@ snapshots: '@eslint/object-schema@2.1.4': optional: true - '@eslint/plugin-kit@0.2.0': + '@eslint/plugin-kit@0.2.3': dependencies: levn: 0.4.1 optional: true @@ -14789,7 +14789,7 @@ snapshots: create-require@1.1.1: {} - cross-spawn@7.0.3: + cross-spawn@7.0.5: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 @@ -15662,7 +15662,7 @@ snapshots: '@ungap/structured-clone': 1.2.0 ajv: 6.12.6 chalk: 4.1.2 - cross-spawn: 7.0.3 + cross-spawn: 7.0.5 debug: 4.3.7(supports-color@8.1.1) doctrine: 3.0.0 escape-string-regexp: 4.0.0 @@ -15701,7 +15701,7 @@ snapshots: '@eslint/core': 0.6.0 '@eslint/eslintrc': 3.1.0 '@eslint/js': 9.12.0 - '@eslint/plugin-kit': 0.2.0 + '@eslint/plugin-kit': 0.2.3 '@humanfs/node': 0.16.5 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.3.1 @@ -15709,7 +15709,7 @@ snapshots: '@types/json-schema': 7.0.15 ajv: 6.12.6 chalk: 4.1.2 - cross-spawn: 7.0.3 + cross-spawn: 7.0.5 debug: 4.3.7(supports-color@8.1.1) escape-string-regexp: 4.0.0 eslint-scope: 8.1.0 @@ -15814,7 +15814,7 @@ snapshots: execa@5.1.1: dependencies: - cross-spawn: 7.0.3 + cross-spawn: 7.0.5 get-stream: 6.0.1 human-signals: 2.1.0 is-stream: 2.0.1 @@ -16044,7 +16044,7 @@ snapshots: foreground-child@3.3.0: dependencies: - cross-spawn: 7.0.3 + cross-spawn: 7.0.5 signal-exit: 4.1.0 fork-ts-checker-webpack-plugin@6.5.3(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.3)(webpack@5.95.0(esbuild@0.24.0)): @@ -19680,7 +19680,7 @@ snapshots: address: 1.2.2 browserslist: 4.24.0 chalk: 4.1.2 - cross-spawn: 7.0.3 + cross-spawn: 7.0.5 detect-port-alt: 1.1.6 escape-string-regexp: 4.0.0 filesize: 8.0.7 @@ -21460,7 +21460,7 @@ snapshots: '@webpack-cli/serve': 2.0.5(webpack-cli@5.1.4)(webpack-dev-server@5.1.0)(webpack@5.95.0) colorette: 2.0.20 commander: 10.0.1 - cross-spawn: 7.0.3 + cross-spawn: 7.0.5 envinfo: 7.14.0 fastest-levenshtein: 1.0.16 import-local: 3.2.0 diff --git a/queries/javascript.core.scm b/queries/javascript.core.scm index 41ac239df0..24d55cbee1 100644 --- a/queries/javascript.core.scm +++ b/queries/javascript.core.scm @@ -272,6 +272,19 @@ (#has-multiple-children-of-type? @_dummy variable_declarator) ) +;;!! let foo, bar; +;;! ^^^ ^^^ +( + (lexical_declaration + (variable_declarator)? @_.leading.endOf + . + (variable_declarator) @collectionItem + . + (variable_declarator)? @_.trailing.startOf + ) + (#insertion-delimiter! @collectionItem ", ") +) + (expression_statement [ ;; name: diff --git a/queries/rust.scm b/queries/rust.scm index a49b0aba0f..22dfafefce 100644 --- a/queries/rust.scm +++ b/queries/rust.scm @@ -5,14 +5,19 @@ (if_let_expression) ] @ifStatement +;;!! "hello" ( - [ - (raw_string_literal) - (string_literal) - ] @string @textFragment + (string_literal) @string @textFragment (#child-range! @textFragment 0 -1 true true) ) +;;!! r#"foobar"# +( + (raw_string_literal) @string @textFragment + (#shrink-to-match! @textFragment "r#+\"(?.*)\"#+") + +) + [ (line_comment) (block_comment)