Skip to content

Commit a8ea10d

Browse files
authored
Make BaseTarget parameters getters stricter (#1534)
## Context #1520 (comment) ## What This adds a generic to `BaseTarget`, which is expected to receive the type of the subclass' parameters. This tries its best to enforce that is the case, and ensures implementations of `getCloneParameters` return that declared type. ## 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
1 parent b24f061 commit a8ea10d

15 files changed

+47
-30
lines changed

packages/cursorless-engine/src/processTargets/targets/BaseTarget.ts

+22-11
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,32 @@ import {
1515
createContinuousRangeUntypedTarget,
1616
} from "../targetUtil/createContinuousRange";
1717

18-
/** Parameters supported by most target classes */
19-
export interface CommonTargetParameters {
18+
/** Parameters supported by all target classes */
19+
export interface MinimumTargetParameters {
2020
readonly editor: TextEditor;
2121
readonly isReversed: boolean;
22-
readonly contentRange: Range;
2322
readonly thatTarget?: Target;
2423
}
2524

25+
/** Parameters supported by most target classes */
26+
export interface CommonTargetParameters extends MinimumTargetParameters {
27+
readonly contentRange: Range;
28+
}
29+
2630
export interface CloneWithParameters {
2731
readonly thatTarget?: Target;
2832
readonly contentRange?: Range;
2933
}
3034

31-
export default abstract class BaseTarget implements Target {
35+
/**
36+
* An abstract target. All targets subclass this.
37+
*
38+
* @template TParameters The constructor parameters.
39+
*/
40+
export default abstract class BaseTarget<
41+
in out TParameters extends MinimumTargetParameters,
42+
> implements Target
43+
{
3244
protected readonly state: CommonTargetParameters;
3345
isLine = false;
3446
isToken = true;
@@ -39,7 +51,7 @@ export default abstract class BaseTarget implements Target {
3951
isNotebookCell = false;
4052
isWord = false;
4153

42-
constructor(parameters: CommonTargetParameters) {
54+
constructor(parameters: TParameters & CommonTargetParameters) {
4355
this.state = {
4456
editor: parameters.editor,
4557
isReversed: parameters.isReversed,
@@ -112,16 +124,16 @@ export default abstract class BaseTarget implements Target {
112124
throw new NoContainingScopeError("boundary");
113125
}
114126

115-
readonly cloneWith = (parameters: CloneWithParameters) => {
127+
private cloneWith(parameters: CloneWithParameters) {
116128
const constructor = Object.getPrototypeOf(this).constructor;
117129

118130
return new constructor({
119131
...this.getCloneParameters(),
120132
...parameters,
121133
});
122-
};
134+
}
123135

124-
protected abstract getCloneParameters(): object;
136+
protected abstract getCloneParameters(): TParameters;
125137

126138
createContinuousRangeTarget(
127139
isReversed: boolean,
@@ -170,9 +182,8 @@ export default abstract class BaseTarget implements Target {
170182
*
171183
* @returns The object to be used for determining equality
172184
*/
173-
protected getEqualityParameters(): object {
174-
const { thatTarget, ...otherCloneParameters } =
175-
this.getCloneParameters() as { thatTarget?: Target };
185+
protected getEqualityParameters(): Omit<TParameters, "thatTarget"> {
186+
const { thatTarget, ...otherCloneParameters } = this.getCloneParameters();
176187

177188
return {
178189
...otherCloneParameters,

packages/cursorless-engine/src/processTargets/targets/DocumentTarget.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { shrinkRangeToFitContent } from "../../util/selectionUtils";
33
import BaseTarget, { CommonTargetParameters } from "./BaseTarget";
44
import PlainTarget from "./PlainTarget";
55

6-
export default class DocumentTarget extends BaseTarget {
6+
export default class DocumentTarget extends BaseTarget<CommonTargetParameters> {
77
insertionDelimiter = "\n";
88
isLine = true;
99

packages/cursorless-engine/src/processTargets/targets/ImplicitTarget.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { CommonTargetParameters } from "./BaseTarget";
12
import BaseTarget from "./BaseTarget";
23

34
/**
@@ -6,7 +7,7 @@ import BaseTarget from "./BaseTarget";
67
* - The implicit destination in the command `"bring air"`
78
* - The implicit anchor in the range `"take past air"`
89
*/
9-
export default class ImplicitTarget extends BaseTarget {
10+
export default class ImplicitTarget extends BaseTarget<CommonTargetParameters> {
1011
insertionDelimiter = "";
1112
isRaw = true;
1213
hasExplicitScopeType = false;

packages/cursorless-engine/src/processTargets/targets/InteriorTarget.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ import {
66
createContinuousRangeFromRanges,
77
createContinuousRangeUntypedTarget,
88
} from "../targetUtil/createContinuousRange";
9-
import BaseTarget, { CommonTargetParameters } from "./BaseTarget";
9+
import type { MinimumTargetParameters } from "./BaseTarget";
10+
import BaseTarget from "./BaseTarget";
1011

11-
export interface InteriorTargetParameters
12-
extends Omit<CommonTargetParameters, "contentRange"> {
12+
export interface InteriorTargetParameters extends MinimumTargetParameters {
1313
readonly fullInteriorRange: Range;
1414
}
1515

16-
export default class InteriorTarget extends BaseTarget {
16+
export default class InteriorTarget extends BaseTarget<InteriorTargetParameters> {
1717
insertionDelimiter = " ";
1818
private readonly fullInteriorRange: Range;
1919

packages/cursorless-engine/src/processTargets/targets/LineTarget.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import { Target } from "../../typings/target.types";
33
import { expandToFullLine } from "../../util/rangeUtils";
44
import { tryConstructPlainTarget } from "../../util/tryConstructTarget";
55
import { createContinuousLineRange } from "../targetUtil/createContinuousRange";
6+
import type { CommonTargetParameters } from "./BaseTarget";
67
import BaseTarget from "./BaseTarget";
78

8-
export default class LineTarget extends BaseTarget {
9+
export default class LineTarget extends BaseTarget<CommonTargetParameters> {
910
insertionDelimiter = "\n";
1011
isLine = true;
1112

packages/cursorless-engine/src/processTargets/targets/NotebookCellTarget.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { TargetPosition } from "@cursorless/common";
33
import BaseTarget, { CommonTargetParameters } from "./BaseTarget";
44
import { removalUnsupportedForPosition } from "./PositionTarget";
55

6-
export default class NotebookCellTarget extends BaseTarget {
6+
export default class NotebookCellTarget extends BaseTarget<CommonTargetParameters> {
77
insertionDelimiter = "\n";
88
isNotebookCell = true;
99

@@ -32,7 +32,7 @@ interface NotebookCellPositionTargetParameters extends CommonTargetParameters {
3232
readonly position: TargetPosition;
3333
}
3434

35-
export class NotebookCellPositionTarget extends BaseTarget {
35+
export class NotebookCellPositionTarget extends BaseTarget<NotebookCellPositionTargetParameters> {
3636
insertionDelimiter = "\n";
3737
isNotebookCell = true;
3838
public position: TargetPosition;

packages/cursorless-engine/src/processTargets/targets/ParagraphTarget.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ import { expandToFullLine } from "../../util/rangeUtils";
1010
import { constructLineTarget } from "../../util/tryConstructTarget";
1111
import { isSameType } from "../../util/typeUtils";
1212
import { createContinuousLineRange } from "../targetUtil/createContinuousRange";
13+
import type { CommonTargetParameters } from "./BaseTarget";
1314
import BaseTarget from "./BaseTarget";
1415
import LineTarget from "./LineTarget";
1516

16-
export default class ParagraphTarget extends BaseTarget {
17+
export default class ParagraphTarget extends BaseTarget<CommonTargetParameters> {
1718
insertionDelimiter = "\n\n";
1819
isLine = true;
1920

packages/cursorless-engine/src/processTargets/targets/PlainTarget.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ interface PlainTargetParameters extends CommonTargetParameters {
88
* A target that has no leading or trailing delimiters so it's removal range
99
* just consists of the content itself. Its insertion delimiter is empty string.
1010
*/
11-
export default class PlainTarget extends BaseTarget {
11+
export default class PlainTarget extends BaseTarget<PlainTargetParameters> {
1212
insertionDelimiter = "";
1313

1414
constructor(parameters: PlainTargetParameters) {

packages/cursorless-engine/src/processTargets/targets/PositionTarget.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ interface PositionTargetParameters extends CommonTargetParameters {
1010
readonly isRaw: boolean;
1111
}
1212

13-
export default class PositionTarget extends BaseTarget {
13+
export default class PositionTarget extends BaseTarget<PositionTargetParameters> {
1414
insertionDelimiter: string;
1515
isRaw: boolean;
1616
private position: TargetPosition;

packages/cursorless-engine/src/processTargets/targets/RawSelectionTarget.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
import type { CommonTargetParameters } from "./BaseTarget";
12
import BaseTarget from "./BaseTarget";
23

34
/**
45
* A target that has no leading or trailing delimiters so it's removal range
56
* just consists of the content itself. Its insertion delimiter will be
67
* inherited from the source in the case of a bring after a bring before
78
*/
8-
export default class RawSelectionTarget extends BaseTarget {
9+
export default class RawSelectionTarget extends BaseTarget<CommonTargetParameters> {
910
insertionDelimiter = "";
1011
isRaw = true;
1112
isToken = false;

packages/cursorless-engine/src/processTargets/targets/ScopeTypeTarget.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export interface ScopeTypeTargetParameters extends CommonTargetParameters {
2525
readonly trailingDelimiterRange?: Range;
2626
}
2727

28-
export default class ScopeTypeTarget extends BaseTarget {
28+
export default class ScopeTypeTarget extends BaseTarget<ScopeTypeTargetParameters> {
2929
private scopeTypeType_: SimpleScopeTypeType;
3030
private removalRange_?: Range;
3131
private interiorRange_?: Range;

packages/cursorless-engine/src/processTargets/targets/SubTokenWordTarget.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export interface SubTokenTargetParameters extends CommonTargetParameters {
99
readonly trailingDelimiterRange?: Range;
1010
}
1111

12-
export default class SubTokenWordTarget extends BaseTarget {
12+
export default class SubTokenWordTarget extends BaseTarget<SubTokenTargetParameters> {
1313
private leadingDelimiterRange_?: Range;
1414
private trailingDelimiterRange_?: Range;
1515
insertionDelimiter: string;
@@ -43,11 +43,12 @@ export default class SubTokenWordTarget extends BaseTarget {
4343
return getDelimitedSequenceRemovalRange(this);
4444
}
4545

46-
protected getCloneParameters() {
46+
protected getCloneParameters(): SubTokenTargetParameters {
4747
return {
4848
...this.state,
4949
leadingDelimiterRange: this.leadingDelimiterRange_,
5050
trailingDelimiterRange: this.trailingDelimiterRange_,
51+
insertionDelimiter: this.insertionDelimiter,
5152
};
5253
}
5354
}

packages/cursorless-engine/src/processTargets/targets/SurroundingPairTarget.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ interface SurroundingPairTargetParameters extends CommonTargetParameters {
2525
readonly boundary: [Range, Range];
2626
}
2727

28-
export default class SurroundingPairTarget extends BaseTarget {
28+
export default class SurroundingPairTarget extends BaseTarget<SurroundingPairTargetParameters> {
2929
insertionDelimiter = " ";
3030
private interiorRange_: Range;
3131
private boundary_: [Range, Range];

packages/cursorless-engine/src/processTargets/targets/TokenTarget.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import {
66
getTokenRemovalRange,
77
getTokenTrailingDelimiterTarget,
88
} from "../targetUtil/insertionRemovalBehaviors/TokenInsertionRemovalBehavior";
9+
import type { CommonTargetParameters } from "./BaseTarget";
910

10-
export default class TokenTarget extends BaseTarget {
11+
export default class TokenTarget extends BaseTarget<CommonTargetParameters> {
1112
insertionDelimiter = " ";
1213

1314
getLeadingDelimiterTarget(): Target | undefined {

packages/cursorless-engine/src/processTargets/targets/UntypedTarget.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ interface UntypedTargetParameters extends CommonTargetParameters {
1818
* - Use token delimiters (space) for removal and insertion
1919
* - Expand to nearest containing pair when asked for boundary or interior
2020
*/
21-
export default class UntypedTarget extends BaseTarget {
21+
export default class UntypedTarget extends BaseTarget<UntypedTargetParameters> {
2222
insertionDelimiter = " ";
2323
hasExplicitScopeType = false;
2424

0 commit comments

Comments
 (0)