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/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/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/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: