Skip to content

Commit 7a151fb

Browse files
committed
Fixes
1 parent 4563f38 commit 7a151fb

File tree

2 files changed

+64
-20
lines changed

2 files changed

+64
-20
lines changed

packages/cursorless-engine/src/generateSpokenForm/generateSpokenForm.ts

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import {
55
InsertionMode,
66
PartialTargetDescriptor,
77
ScopeType,
8+
camelCaseToAllDown,
89
} from "@cursorless/common";
9-
import { RecursiveArray } from "lodash";
1010
import { NoSpokenFormError } from "./NoSpokenFormError";
1111
import { actions } from "./defaultSpokenForms/actions";
1212
import { connectives } from "./defaultSpokenForms/connectives";
@@ -174,9 +174,7 @@ export class SpokenFormGenerator {
174174
}
175175
}
176176

177-
private handleTarget(
178-
target: PartialTargetDescriptor,
179-
): RecursiveArray<string> {
177+
private handleTarget(target: PartialTargetDescriptor): SpokenFormComponent {
180178
switch (target.type) {
181179
case "list":
182180
if (target.elements.length < 2) {
@@ -210,7 +208,7 @@ export class SpokenFormGenerator {
210208

211209
private handleDestination(
212210
destination: DestinationDescriptor,
213-
): RecursiveArray<string> {
211+
): SpokenFormComponent {
214212
switch (destination.type) {
215213
case "list":
216214
if (destination.destinations.length < 2) {
@@ -252,12 +250,41 @@ function constructSpokenForms(component: SpokenFormComponent): string[] {
252250
}
253251

254252
if (Array.isArray(component)) {
255-
return constructSpokenFormsArray(component);
253+
return cartesianProduct(component.map(constructSpokenForms)).map((words) =>
254+
words.join(" "),
255+
);
256256
}
257+
258+
if (component.spokenForms.length === 0) {
259+
throw new NoSpokenFormError(
260+
`${camelCaseToAllDown(component.spokenFormType)} with id ${
261+
component.id
262+
}; please see https://www.cursorless.org/docs/user/customization/ for more information`,
263+
);
264+
}
265+
266+
return component.spokenForms;
257267
}
258268

259-
function cartesianProduct<T>(...arrays: T[][]): T[] {
260-
return arrays.reduce((acc, val) =>
261-
acc.flatMap((x) => val.map((y) => [...x, y])),
269+
/**
270+
* Given an array of arrays, constructs all possible combinations of the
271+
* elements of the arrays. For example, given [[1, 2], [3, 4]], returns [[1, 3],
272+
* [1, 4], [2, 3], [2, 4]]. If any of the arrays are empty, returns an empty
273+
* array.
274+
* @param arrays The arrays to take the cartesian product of
275+
*/
276+
function cartesianProduct<T>(arrays: T[][]): T[][] {
277+
if (arrays.length === 0) {
278+
return [];
279+
}
280+
281+
if (arrays.length === 1) {
282+
return arrays[0].map((element) => [element]);
283+
}
284+
285+
const [first, ...rest] = arrays;
286+
const restCartesianProduct = cartesianProduct(rest);
287+
return first.flatMap((element) =>
288+
restCartesianProduct.map((restElement) => [element, ...restElement]),
262289
);
263290
}

packages/cursorless-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import {
2020
numberToSpokenForm,
2121
ordinalToSpokenForm,
2222
} from "./defaultSpokenForms/numbers";
23-
import { surroundingPairNameToSpokenForm } from "./defaultSpokenForms/modifiers";
2423
import { characterToSpokenForm } from "./defaultSpokenForms/characters";
2524
import {
2625
GeneratorSpokenFormMap,
@@ -35,7 +34,7 @@ export class PrimitiveTargetSpokenFormGenerator {
3534
handlePrimitiveTarget(
3635
target: PartialPrimitiveTargetDescriptor,
3736
): SpokenFormComponent {
38-
const components: SpokenFormComponents = [];
37+
const components: SpokenFormComponent[] = [];
3938
if (target.modifiers != null) {
4039
components.push(target.modifiers.map(this.handleModifier));
4140
}
@@ -147,7 +146,7 @@ export class PrimitiveTargetSpokenFormGenerator {
147146

148147
private handleRelativeScopeInclusive(
149148
modifier: RelativeScopeModifier,
150-
): SpokenFormComponents {
149+
): SpokenFormComponent {
151150
const scope = this.handleScopeType(modifier.scopeType);
152151

153152
if (modifier.length === 1) {
@@ -175,7 +174,7 @@ export class PrimitiveTargetSpokenFormGenerator {
175174

176175
private handleRelativeScopeExclusive(
177176
modifier: RelativeScopeModifier,
178-
): SpokenFormComponents {
177+
): SpokenFormComponent {
179178
const scope = this.handleScopeType(modifier.scopeType);
180179
const direction =
181180
modifier.direction === "forward"
@@ -207,7 +206,7 @@ export class PrimitiveTargetSpokenFormGenerator {
207206
);
208207
}
209208

210-
handleScopeType(scopeType: ScopeType): SpokenForms {
209+
handleScopeType(scopeType: ScopeType): SpokenFormComponent {
211210
switch (scopeType.type) {
212211
case "oneOf":
213212
case "customRegex":
@@ -220,10 +219,7 @@ export class PrimitiveTargetSpokenFormGenerator {
220219
`Scope type '${scopeType.type}' with delimiter 'collectionBoundary'`,
221220
);
222221
}
223-
const pair = surroundingPairNameToSpokenForm(
224-
this.spokenFormMap,
225-
scopeType.delimiter,
226-
);
222+
const pair = this.spokenFormMap.pairedDelimiter[scopeType.delimiter];
227223
if (scopeType.forceDirection != null) {
228224
const direction =
229225
scopeType.forceDirection === "left"
@@ -239,7 +235,7 @@ export class PrimitiveTargetSpokenFormGenerator {
239235
}
240236
}
241237

242-
private handleMark(mark: PartialMark): SpokenFormComponents {
238+
private handleMark(mark: PartialMark): SpokenFormComponent {
243239
switch (mark.type) {
244240
case "decoratedSymbol": {
245241
const [color, shape] = mark.symbolColor.split("-");
@@ -314,7 +310,28 @@ export class PrimitiveTargetSpokenFormGenerator {
314310
}
315311
}
316312

313+
function pluralize(name: SpokenFormComponent): SpokenFormComponent {
314+
if (typeof name === "string") {
315+
return pluralizeString(name);
316+
}
317+
318+
if (Array.isArray(name)) {
319+
if (name.length === 0) {
320+
return name;
321+
}
322+
323+
const last = name[name.length - 1];
324+
325+
return [...name.slice(0, -1), pluralize(last)];
326+
}
327+
328+
return {
329+
...name,
330+
spokenForms: name.spokenForms.map(pluralizeString),
331+
};
332+
}
333+
317334
// FIXME: Properly pluralize
318-
function pluralize(name: string): string {
335+
function pluralizeString(name: string): string {
319336
return `${name}s`;
320337
}

0 commit comments

Comments
 (0)