Skip to content

Commit

Permalink
Scope tests (#2053)
Browse files Browse the repository at this point in the history
Started implementing a new scope handler test representation


```
[Content]
const name = "Hello world";
              ^^^^^^^^^^^^^
```

Originally I was thinking of doing `^` for each character. That worked
fine until @pokey asked the question about empty ranges which we
actually has in a few places. So I switch to a multiline representation
of `[---]` where the empty range would be `[]`.


```
[Content]
 function myFunk() {
[-------------------
 
 }
 -]
```

I do wonder if we want to start multiline ranges above the source code
and end it below. Makes it so the code is actually inside the range
endings.

```
[Content]
[-------------------
 function myFunk() {
 
 }
 -]
```

Relevant issues
#1524
#2010

## 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>
  • Loading branch information
3 people authored Dec 6, 2023
1 parent 740e3ef commit 43cf0da
Show file tree
Hide file tree
Showing 32 changed files with 1,344 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ repos:
# tests use strings with trailing white space to represent the final
# document contents. For example
# packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/ruby/changeCondition.yml
exclude: ^packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/.*/[^/]*\.yml$|/generated/|^patches/
exclude: ^packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/.*/[^/]*\.yml$|\.scope$|/generated/|^patches/
- repo: local
hooks:
- id: eslint
Expand Down
4 changes: 4 additions & 0 deletions packages/common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,7 @@ export * from "./types/TestCaseFixture";
export * from "./util/getEnvironmentVariableStrict";
export * from "./util/CompositeKeyDefaultMap";
export * from "./util/toPlainObject";
export * from "./scopeSupportFacets/scopeSupportFacets.types";
export * from "./scopeSupportFacets/scopeSupportFacetInfos";
export * from "./scopeSupportFacets/textualScopeSupportFacetInfos";
export * from "./scopeSupportFacets/getLanguageScopeSupport";
15 changes: 15 additions & 0 deletions packages/common/src/scopeSupportFacets/getLanguageScopeSupport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { htmlScopeSupport } from "./html";
import { javascriptScopeSupport } from "./javascript";
import { LanguageScopeSupportFacetMap } from "./scopeSupportFacets.types";

export function getLanguageScopeSupport(
languageId: string,
): LanguageScopeSupportFacetMap {
switch (languageId) {
case "javascript":
return javascriptScopeSupport;
case "html":
return htmlScopeSupport;
}
throw Error(`Unsupported language: '${languageId}'`);
}
19 changes: 19 additions & 0 deletions packages/common/src/scopeSupportFacets/html.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {
LanguageScopeSupportFacetMap,
ScopeSupportFacetLevel,
} from "./scopeSupportFacets.types";

const { supported, notApplicable } = ScopeSupportFacetLevel;

export const htmlScopeSupport: LanguageScopeSupportFacetMap = {
["key.attribute"]: supported,
["tags"]: supported,

namedFunction: notApplicable,
["name.assignment"]: notApplicable,
["key.mapPair"]: notApplicable,
["key.mapPair.iteration"]: notApplicable,
["value.mapPair"]: notApplicable,
["value.mapPair.iteration"]: notApplicable,
["value.assignment"]: notApplicable,
};
19 changes: 19 additions & 0 deletions packages/common/src/scopeSupportFacets/javascript.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {
LanguageScopeSupportFacetMap,
ScopeSupportFacetLevel,
} from "./scopeSupportFacets.types";

const { supported, notApplicable } = ScopeSupportFacetLevel;

export const javascriptScopeSupport: LanguageScopeSupportFacetMap = {
namedFunction: supported,
["name.assignment"]: supported,
["key.mapPair"]: supported,
["key.mapPair.iteration"]: supported,
["value.mapPair"]: supported,
["value.mapPair.iteration"]: supported,
["value.assignment"]: supported,

["key.attribute"]: notApplicable,
["tags"]: notApplicable,
};
48 changes: 48 additions & 0 deletions packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {
ScopeSupportFacet,
ScopeSupportFacetInfo,
} from "./scopeSupportFacets.types";

export const scopeSupportFacetInfos: Record<
ScopeSupportFacet,
ScopeSupportFacetInfo
> = {
namedFunction: {
description: "A named function",
scopeType: "namedFunction",
},
["name.assignment"]: {
description: "Name(LHS) of an assignment",
scopeType: "name",
},
["key.attribute"]: {
description: "Key(LHS) of an attribute",
scopeType: "collectionKey",
},
["key.mapPair"]: {
description: "Key(LHS) of a map pair",
scopeType: "collectionKey",
},
["key.mapPair.iteration"]: {
description: "Iteration of map pair keys",
scopeType: "collectionKey",
isIteration: true,
},
["value.assignment"]: {
description: "Value(RHS) of an assignment",
scopeType: "value",
},
["value.mapPair"]: {
description: "Key(RHS) of a map pair",
scopeType: "value",
},
["value.mapPair.iteration"]: {
description: "Iteration of map pair values",
scopeType: "value",
isIteration: true,
},
["tags"]: {
description: "Both tags in an xml element",
scopeType: "xmlBothTags",
},
};
80 changes: 80 additions & 0 deletions packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { SimpleScopeTypeType } from "../types/command/PartialTargetDescriptor.types";

const scopeSupportFacets = [
// "list",
// "list.interior",
// "map",
// "map.interior",
// "collectionKey",
"namedFunction",
// "namedFunction.interior",
// "functionName",
// "anonymousFunction",
// "anonymousFunction.interior",
"name.assignment",
"key.attribute",
"key.mapPair",
"key.mapPair.iteration",
"value.assignment",
"value.mapPair",
"value.mapPair.iteration",
// "value.assignment.removal",
// "value.return",
// "value.return.removal",
// "value.collectionItem",
// "value.collectionItem.removal",
// "statement",
// "ifStatement",
// "condition.if",
// "condition.while",
// "condition.doWhile",
// "condition.for",
// "condition.ternary",
// "branch",
// "comment.line",
// "comment.block",
// "string.singleLine",
// "string.multiLine",
// "textFragment",
// "functionCall",
// "functionCallee",
// "argumentOrParameter.argument",
// "argumentOrParameter.argument.removal",
// "argumentOrParameter.parameter",
// "argumentOrParameter.parameter.removal",
// "class",
// "class.interior",
// "className",
// "type",
"tags",
] as const;

const textualScopeSupportFacets = [
"character",
"word",
"token",
"line",
"paragraph",
"document",
] as const;

export interface ScopeSupportFacetInfo {
readonly description: string;
readonly scopeType: SimpleScopeTypeType;
readonly isIteration?: boolean;
}

export enum ScopeSupportFacetLevel {
supported,
unsupported,
notApplicable,
}

export type ScopeSupportFacet = (typeof scopeSupportFacets)[number];

export type TextualScopeSupportFacet =
(typeof textualScopeSupportFacets)[number];

export type LanguageScopeSupportFacetMap = Partial<
Record<ScopeSupportFacet, ScopeSupportFacetLevel>
>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {
ScopeSupportFacetInfo,
TextualScopeSupportFacet,
} from "./scopeSupportFacets.types";

export const textualScopeSupportFacetInfos: Record<
TextualScopeSupportFacet,
ScopeSupportFacetInfo
> = {
character: {
description: "A single character in the document",
scopeType: "character",
},
word: {
description: "A single word in a token",
scopeType: "word",
},
token: {
description: "A single token in the document",
scopeType: "token",
},
line: {
description: "A single line in the document",
scopeType: "line",
},
paragraph: {
description:
"A single paragraph(contiguous block of lines) in the document",
scopeType: "paragraph",
},
document: {
description: "The entire document",
scopeType: "document",
},
};
18 changes: 18 additions & 0 deletions packages/common/src/testUtil/getFixturePaths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export function getRecordedTestsDirPath() {
return path.join(getFixturesPath(), "recorded");
}

export function getScopeTestsDirPath() {
return path.join(getFixturesPath(), "scopes");
}

export function getRecordedTestPaths() {
const directory = getRecordedTestsDirPath();
const relativeDir = path.dirname(directory);
Expand All @@ -34,3 +38,17 @@ export function getRecordedTestPaths() {
path: p,
}));
}

export function getScopeTestPaths() {
const directory = getScopeTestsDirPath();
const relativeDir = path.dirname(directory);

return walkFilesSync(directory)
.filter((p) => p.endsWith(".scope"))
.map((p) => ({
path: p,
name: path.relative(relativeDir, p.substring(0, p.lastIndexOf("."))),
languageId: path.dirname(path.relative(directory, p)).split(path.sep)[0],
facet: path.basename(p).match(/([a-zA-Z.]+)\d*\.scope/)![1],
}));
}
6 changes: 6 additions & 0 deletions packages/common/src/types/ScopeProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,13 @@ export interface ScopeRanges {
*/
export interface TargetRanges {
contentRange: Range;
removalRange: Range;
removalHighlightRange: GeneralizedRange;
leadingDelimiter: TargetRanges | undefined;
trailingDelimiter: TargetRanges | undefined;
interior: TargetRanges[] | undefined;
boundary: TargetRanges[] | undefined;
insertionDelimiter: string;
}

/**
Expand Down
26 changes: 26 additions & 0 deletions packages/cursorless-engine/src/scopeProviders/getTargetRanges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,36 @@ import {
import { Target } from "../typings/target.types";

export function getTargetRanges(target: Target): TargetRanges {
const interior = (() => {
try {
target.getInteriorStrict().map(getTargetRanges);
} catch (error) {
return undefined;
}
})();

const boundary = (() => {
try {
target.getBoundaryStrict().map(getTargetRanges);
} catch (error) {
return undefined;
}
})();

return {
contentRange: target.contentRange,
removalRange: target.getRemovalRange(),
removalHighlightRange: target.isLine
? toLineRange(target.getRemovalHighlightRange())
: toCharacterRange(target.getRemovalHighlightRange()),
leadingDelimiter: getOptionalTarget(target.getLeadingDelimiterTarget()),
trailingDelimiter: getOptionalTarget(target.getTrailingDelimiterTarget()),
interior,
boundary,
insertionDelimiter: target.insertionDelimiter,
};
}

function getOptionalTarget(target: Target | undefined) {
return target != null ? getTargetRanges(target) : undefined;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<div id="root"></div>
---

[Content] = 0:5-0:7
0| <div id="root"></div>
>--<

[Removal] = 0:5-0:8
0| <div id="root"></div>
>---<

[Trailing delimiter] = 0:7-0:8
0| <div id="root"></div>
>-<

[Domain] = 0:5-0:14
0| <div id="root"></div>
>---------<

[Insertion delimiter] = " "
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<div>hello</div>
---

[.1 Content] =
[.1 Removal] = 0:0-0:5
0| <div>hello</div>
>-----<

[.1 Insertion delimiter] = " "

[.2 Content] =
[.2 Removal] = 0:10-0:16
0| <div>hello</div>
>------<

[.2 Insertion delimiter] = " "

[Domain] = 0:0-0:16
0| <div>hello</div>
>----------------<
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{ value: 123 }
---

[Range] =
[Domain] = 0:1-0:13
0| { value: 123 }
>------------<
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{ value: 123 }
---

[Content] = 0:2-0:7
0| { value: 123 }
>-----<

[Removal] = 0:2-0:9
0| { value: 123 }
>-------<

[Trailing delimiter] = 0:7-0:9
0| { value: 123 }
>--<

[Domain] = 0:2-0:12
0| { value: 123 }
>----------<

[Insertion delimiter] = " "
Loading

0 comments on commit 43cf0da

Please sign in to comment.