From 240fd11ed81b06fbd53e9e90c0de015091a4e31f Mon Sep 17 00:00:00 2001 From: lingdocs <71590811+lingdocs@users.noreply.github.com> Date: Wed, 4 May 2022 10:47:27 -0500 Subject: [PATCH] refactoring the EP and VP explorers - hoping to get proper robust mini pronoun stuff happening soon. also fixed a problem with the EPExplorers, in EPs you omit the SUBJECT always not the king --- package.json | 2 +- src/components/ep-explorer/EPDisplay.tsx | 24 ++- src/components/ep-explorer/EPExplorer.tsx | 175 +++-------------- src/components/ep-explorer/eps-reducer.ts | 160 +++++++++++++++ src/components/vp-explorer/TensePicker.tsx | 87 ++------- src/components/vp-explorer/VPExplorer.tsx | 81 +++----- src/components/vp-explorer/VerbPicker.tsx | 51 ++--- src/components/vp-explorer/vps-reducer.ts | 215 +++++++++++++++++++++ src/lib/phrase-building/compile-ep.ts | 28 ++- src/lib/phrase-building/compile-tools.ts | 61 ++++-- src/lib/phrase-building/compile-vp.ts | 4 +- src/lib/phrase-building/render-ep.ts | 2 +- src/lib/useStickyState.ts | 18 +- src/types.ts | 8 +- 14 files changed, 564 insertions(+), 352 deletions(-) create mode 100644 src/components/ep-explorer/eps-reducer.ts create mode 100644 src/components/vp-explorer/vps-reducer.ts diff --git a/package.json b/package.json index 4c8be58..fee8412 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lingdocs/pashto-inflector", - "version": "2.3.8", + "version": "2.3.9", "author": "lingdocs.com", "description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations", "homepage": "https://verbs.lingdocs.com", diff --git a/src/components/ep-explorer/EPDisplay.tsx b/src/components/ep-explorer/EPDisplay.tsx index 351cb6b..c6912ab 100644 --- a/src/components/ep-explorer/EPDisplay.tsx +++ b/src/components/ep-explorer/EPDisplay.tsx @@ -1,13 +1,13 @@ import * as T from "../../types"; import { renderEP } from "../../lib/phrase-building/render-ep"; import { compileEP } from "../../lib/phrase-building/compile-ep"; -import AbbreviationFormSelector from "../vp-explorer/AbbreviationFormSelector"; import Examples from "../Examples"; +import ButtonSelect from "../ButtonSelect"; -function EPDisplay({ eps, opts, setForm }: { +function EPDisplay({ eps, opts, setOmitSubject }: { eps: T.EPSelectionState, opts: T.TextOptions, - setForm: (form: T.FormVersion) => void, + setOmitSubject: (value: "true" | "false") => void, }) { const EP = completeEPSelection(eps); if (!EP) { @@ -22,13 +22,19 @@ function EPDisplay({ eps, opts, setForm }: { } const rendered = renderEP(EP); - const result = compileEP(rendered, EP.form); + const result = compileEP(rendered); return
- +
+ +
{"long" in result.ps ?
diff --git a/src/components/ep-explorer/EPExplorer.tsx b/src/components/ep-explorer/EPExplorer.tsx index d0f37f4..bd22acd 100644 --- a/src/components/ep-explorer/EPExplorer.tsx +++ b/src/components/ep-explorer/EPExplorer.tsx @@ -1,17 +1,27 @@ import * as T from "../../types"; -import useStickyState from "../../lib/useStickyState"; +import useStickyState, { useStickyReducer } from "../../lib/useStickyState"; import NPPicker from "../np-picker/NPPicker"; import EquativePicker from "./EquativePicker"; import EPDisplay from "./EPDisplay"; import ButtonSelect from "../ButtonSelect"; import EqCompPicker from "./eq-comp-picker/EqCompPicker"; import { roleIcon } from "../vp-explorer/VPExplorerExplanationModal"; -import { isUnisexNounEntry } from "../../lib/type-predicates"; -import { - personGender, - personNumber, -} from "../../lib/misc-helpers"; import EqChartsDisplay from "./EqChartsDisplay"; +import epsReducer from "./eps-reducer"; +const blankEps: T.EPSelectionState = { + subject: undefined, + predicate: { + type: "Complement", + NP: undefined, + Complement: undefined, + }, + equative: { + tense: "present", + negative: false, + }, + shrunkenPossesive: undefined, + omitSubject: false, +}; // TODO: put the clear button beside the title in the predicate picker? @@ -20,56 +30,7 @@ function EPExplorer(props: { entryFeeder: T.EntryFeeder, }) { const [mode, setMode] = useStickyState<"charts" | "phrases">("charts", "EPExplorerMode"); - const [eps, setEps] = useStickyState({ - subject: undefined, - predicate: { - type: "Complement", - NP: undefined, - Complement: undefined, - }, - equative: { - tense: "present", - negative: false, - }, - shrunkenPossesive: undefined, - form: { removeKing: false, shrinkServant: false }, - }, "EPSelectionState10"); - function handlePredicateTypeChange(type: "NP" | "Complement") { - setEps(o => ({ - ...o, - predicate: { - ...o.predicate, - type, - }, - })); - } - function handleSetSubject(subject: T.NPSelection | undefined) { - setEps(old => massageSubjectChange(subject, old)); - } - function setPredicateNP(selection: T.NPSelection | undefined) { - setEps(o => massageNPPredicateChange(selection, o)) - } - function setPredicateComp(selection: T.EqCompSelection | undefined) { - setEps(o => ({ - ...o, - predicate: { - ...o.predicate, - Complement: selection - }, - })); - } - function handleShrinkPossesive(shrunkenPossesive: number | undefined) { - setEps(o => ({ - ...o, - shrunkenPossesive, - })); - } - function handleSetForm(form: T.FormVersion) { - setEps(o => ({ - ...o, - form, - })); - } + const [eps, adjustEps] = useStickyReducer(epsReducer, blankEps, "EPSelectionState10"); const king = eps.subject?.type === "pronoun" ? "subject" : eps.predicate.type === "Complement" @@ -91,13 +52,13 @@ function EPExplorer(props: {
adjustEps({ type: "shrink possesive", payload })} heading={
Subject {king === "subject" ? roleIcon.king : ""}
} entryFeeder={props.entryFeeder} np={eps.subject} counterPart={undefined} role="subject" - onChange={handleSetSubject} + onChange={payload => adjustEps({ type: "set subject", payload })} opts={props.opts} />
@@ -111,21 +72,21 @@ function EPExplorer(props: { { value: "Complement", label: "Complement" }, ]} value={eps.predicate.type} - handleChange={handlePredicateTypeChange} + handleChange={payload => adjustEps({ type: "set predicate type", payload })} />
{eps.predicate.type === "NP" ? adjustEps({ type: "shrink possesive", payload })} entryFeeder={props.entryFeeder} np={eps.predicate.type === "NP" ? eps.predicate.NP : undefined} counterPart={undefined} role="subject" - onChange={setPredicateNP} + onChange={payload => adjustEps({ type: "set predicate NP", payload })} opts={props.opts} /> : adjustEps({ type: "set predicate comp", payload })} opts={props.opts} entryFeeder={props.entryFeeder} />} @@ -135,7 +96,7 @@ function EPExplorer(props: {
Equative
setEps(o => ({ ...o, equative }))} + onChange={payload => adjustEps({ type: "set equative", payload })} hideNegative={mode === "charts"} />
@@ -144,95 +105,9 @@ function EPExplorer(props: { {mode === "phrases" && adjustEps({ type: "set omitSubject", payload })} />} ; } export default EPExplorer; - -function massageNPPredicateChange(selection: T.NPSelection | undefined, old: T.EPSelectionState): T.EPSelectionState { - if (!selection) { - return { - ...old, - predicate: { - ...old.predicate, - NP: selection, - }, - }; - } - if (old.subject?.type === "pronoun" && selection.type === "noun" && isUnisexNounEntry(selection.entry)) { - const { gender, number } = selection; - const pronoun = old.subject.person; - const newPronoun = movePersonNumber(movePersonGender(pronoun, gender), number); - return { - ...old, - subject: { - ...old.subject, - person: newPronoun, - }, - predicate: { - ...old.predicate, - NP: selection, - }, - }; - } - return { - ...old, - predicate: { - ...old.predicate, - NP: selection, - }, - }; -} - -function massageSubjectChange(subject: T.NPSelection | undefined, old: T.EPSelectionState): T.EPSelectionState { - if (!subject) { - return { - ...old, - subject, - }; - } - if (subject.type === "pronoun" && old.predicate.type === "NP" && old.predicate.NP?.type === "noun" && isUnisexNounEntry(old.predicate.NP.entry)) { - const predicate = old.predicate.NP; - const adjusted = { - ...predicate, - ...predicate.numberCanChange ? { - number: personNumber(subject.person), - } : {}, - ...predicate.genderCanChange ? { - gender: personGender(subject.person), - } : {}, - } - return { - ...old, - subject, - predicate: { - ...old.predicate, - NP: adjusted, - }, - }; - } - return { - ...old, - subject, - }; -} - -function movePersonGender(p: T.Person, gender: T.Gender): T.Person { - const pGender = personGender(p); - if (gender === pGender) { - return p; - } - return (gender === "masc") ? (p - 1) : (p + 1); -} - -function movePersonNumber(p: T.Person, number: T.NounNumber): T.Person { - const pNumber = personNumber(p); - if (pNumber === number) { - return p; - } - return (number === "plural") - ? (p + 6) - : (p - 6); -} \ No newline at end of file diff --git a/src/components/ep-explorer/eps-reducer.ts b/src/components/ep-explorer/eps-reducer.ts new file mode 100644 index 0000000..9b522ec --- /dev/null +++ b/src/components/ep-explorer/eps-reducer.ts @@ -0,0 +1,160 @@ +import * as T from "../../types"; +import { + personGender, + personNumber, +} from "../../lib/misc-helpers"; +import { isUnisexNounEntry } from "../../lib/type-predicates"; + +export type EpsReducerAction = { + type: "set predicate type", + payload: "NP" | "Complement", +} | { + type: "set subject", + payload: T.NPSelection | undefined, +} | { + type: "set predicate NP", + payload: T.NPSelection | undefined, +} | { + type: "set predicate comp", + payload: T.EqCompSelection | undefined, +} | { + type: "shrink possesive", + payload: number | undefined, +} | { + type: "set omitSubject", + payload: "true" | "false", +} | { + type: "set equative", + payload: T.EquativeSelection, +}; + +export default function epsReducer(eps: T.EPSelectionState, action: EpsReducerAction): T.EPSelectionState { + if (action.type === "set predicate type") { + return { + ...eps, + predicate: { + ...eps.predicate, + type: action.payload, + }, + }; + } + if (action.type === "set subject") { + return massageSubjectChange(action.payload, eps); + } + if (action.type === "set predicate NP") { + return massageNPPredicateChange(action.payload, eps); + } + if (action.type === "set predicate comp") { + return { + ...eps, + predicate: { + ...eps.predicate, + Complement: action.payload, + }, + }; + } + if (action.type === "shrink possesive") { + return { + ...eps, + shrunkenPossesive: action.payload, + }; + } + if (action.type === "set omitSubject") { + return { + ...eps, + omitSubject: action.payload === "true", + }; + } + // if (action.type === "set equative") { + return { + ...eps, + equative: action.payload, + } + // } +} + +function massageSubjectChange(subject: T.NPSelection | undefined, old: T.EPSelectionState): T.EPSelectionState { + if (!subject) { + return { + ...old, + subject, + }; + } + if (subject.type === "pronoun" && old.predicate.type === "NP" && old.predicate.NP?.type === "noun" && isUnisexNounEntry(old.predicate.NP.entry)) { + const predicate = old.predicate.NP; + const adjusted = { + ...predicate, + ...predicate.numberCanChange ? { + number: personNumber(subject.person), + } : {}, + ...predicate.genderCanChange ? { + gender: personGender(subject.person), + } : {}, + } + return { + ...old, + subject, + predicate: { + ...old.predicate, + NP: adjusted, + }, + }; + } + return { + ...old, + subject, + }; +} + +function massageNPPredicateChange(selection: T.NPSelection | undefined, old: T.EPSelectionState): T.EPSelectionState { + if (!selection) { + return { + ...old, + predicate: { + ...old.predicate, + NP: selection, + }, + }; + } + if (old.subject?.type === "pronoun" && selection.type === "noun" && isUnisexNounEntry(selection.entry)) { + const { gender, number } = selection; + const pronoun = old.subject.person; + const newPronoun = movePersonNumber(movePersonGender(pronoun, gender), number); + return { + ...old, + subject: { + ...old.subject, + person: newPronoun, + }, + predicate: { + ...old.predicate, + NP: selection, + }, + }; + } + return { + ...old, + predicate: { + ...old.predicate, + NP: selection, + }, + }; +} + +function movePersonGender(p: T.Person, gender: T.Gender): T.Person { + const pGender = personGender(p); + if (gender === pGender) { + return p; + } + return (gender === "masc") ? (p - 1) : (p + 1); +} + +function movePersonNumber(p: T.Person, number: T.NounNumber): T.Person { + const pNumber = personNumber(p); + if (pNumber === number) { + return p; + } + return (number === "plural") + ? (p + 6) + : (p - 6); +} \ No newline at end of file diff --git a/src/components/vp-explorer/TensePicker.tsx b/src/components/vp-explorer/TensePicker.tsx index 6195d75..92d132d 100644 --- a/src/components/vp-explorer/TensePicker.tsx +++ b/src/components/vp-explorer/TensePicker.tsx @@ -2,9 +2,11 @@ import Select from "react-select"; import * as T from "../../types"; import ButtonSelect from "../ButtonSelect"; import { isImperativeTense, isModalTense, isPerfectTense, isVerbTense } from "../../lib/type-predicates"; -import { ensure2ndPersSubjPronounAndNoConflict } from "../../lib/phrase-building/vp-tools"; import useStickyState from "../../lib/useStickyState"; import { customStyles } from "../EntrySelect"; +import { + VpsReducerAction +} from "./vps-reducer"; const verbTenseOptions: { label: string | JSX.Element, value: T.VerbTense, formula: string, modalFormula: string, }[] = [{ label:
present
, @@ -113,45 +115,17 @@ function TensePicker(props: ({ } | { vpsComplete: T.VPSelectionComplete, }) & { - onChange: (p: T.VPSelectionState) => void, + onChange: (p: VpsReducerAction) => void, mode: "charts" | "phrases" | "quiz", }) { const [showFormula, setShowFormula] = useStickyState(false, "showFormula"); function onTenseSelect(o: { value: T.VerbTense | T.PerfectTense | T.ImperativeTense } | null) { if ("vpsComplete" in props) return; - const value = o?.value ? o.value : undefined; - if (props.vps.verb && value) { - if (isPerfectTense(value)) { - props.onChange({ - ...props.vps, - verb: { - ...props.vps.verb, - perfectTense: value, - tenseCategory: "perfect", - }, - }); - } else if (isImperativeTense(value)) { - props.onChange({ - ...props.vps, - verb: { - ...props.vps.verb, - imperativeTense: value, - tenseCategory: "imperative", - }, - }); - } else { - props.onChange({ - ...props.vps, - verb: { - ...props.vps.verb, - verbTense: value, - tenseCategory: props.vps.verb.tenseCategory === "perfect" - ? "basic" - : props.vps.verb.tenseCategory, - }, - }); - } - } + const tense = o?.value ? o.value : undefined; + props.onChange({ + type: "set tense", + payload: tense, + }); } function moveTense(dir: "forward" | "back") { if ("vpsComplete" in props) return; @@ -182,40 +156,19 @@ function TensePicker(props: ({ onTenseSelect(newTense); }; } - function onPosNegSelect(value: string) { + function onPosNegSelect(payload: "true" | "false") { if ("vpsComplete" in props) return; - if (props.vps.verb) { - props.onChange({ - ...props.vps, - verb: { - ...props.vps.verb, - negative: value === "true", - }, - }); - } + props.onChange({ + type: "set negativity", + payload, + }); } - function onTenseCategorySelect(value: "basic" | "modal" | "perfect" | "imperative") { + function onTenseCategorySelect(payload: "basic" | "modal" | "perfect" | "imperative") { if ("vpsComplete" in props) return; - if (props.vps.verb) { - if (value === "imperative") { - props.onChange(ensure2ndPersSubjPronounAndNoConflict({ - ...props.vps, - verb: { - ...props.vps.verb, - voice: "active", - tenseCategory: value, - }, - })); - return; - } - props.onChange({ - ...props.vps, - verb: { - ...props.vps.verb, - tenseCategory: value, - }, - }); - } + props.onChange({ + type: "set tense category", + payload, + }); } const tOptions = ("vps" in props && (props.vps.verb?.tenseCategory === "perfect")) ? perfectTenseOptions @@ -292,7 +245,7 @@ function TensePicker(props: ({ {props.mode === "phrases" && void) | "none", entryFeeder: T.EntryFeeder, }) { - const [vps, setVps] = useStickyState( + const [vps, adjustVps] = useStickyReducer( + vpsReducer, props.loaded ? props.loaded : savedVps => makeVPSelectionState(props.verb, savedVps), @@ -62,51 +60,41 @@ export function VPExplorer(props: { useEffect(() => { const VPSFromUrl = getVPSFromUrl(); if (VPSFromUrl) { - setVps(VPSFromUrl); + adjustVps({ + type: "load vps", + payload: VPSFromUrl + }); } // eslint-disable-next-line }, []); useEffect(() => { - setVps(oldVps => { - if (mode === "quiz") { - setMode("phrases"); - } - return makeVPSelectionState(props.verb, oldVps); + const newVps = makeVPSelectionState(props.verb, vps); + adjustVps({ + type: "load vps", + payload: newVps, }); // eslint-disable-next-line }, [props.verb]); function handleSubjectChange(subject: T.NPSelection | undefined, skipPronounConflictCheck?: boolean) { - if (!skipPronounConflictCheck && hasPronounConflict(subject, vps.verb?.object)) { - alert("That combination of pronouns is not allowed"); - return; - } - setVps(o => ({ ...o, subject })); + adjustVps({ + type: "set subject", + payload: { subject, skipPronounConflictCheck }, + }); } function handleObjectChange(object: T.NPSelection | undefined) { - if (!vps.verb) return; - if ((vps.verb.object === "none") || (typeof vps.verb.object === "number")) return; - // check for pronoun conflict - if (hasPronounConflict(vps.subject, object)) { - alert("That combination of pronouns is not allowed"); - return; - } - setVps(o => ({ - ...o, - verb: { - ...o.verb, - object, - }, - })); + adjustVps({ + type: "set object", + payload: object, + }); } function handleSubjObjSwap() { - if (vps.verb?.isCompound === "dynamic") return; - setVps(switchSubjObj) + adjustVps({ type: "swap subj/obj" }); } function handleShrinkPossesive(shrunkenPossesive: number | undefined) { - setVps(o => ({ - ...o, - shrunkenPossesive, - })); + adjustVps({ + type: "shrink possesive", + payload: shrunkenPossesive, + }); } function quizLock(f: T) { if (mode === "quiz") { @@ -118,10 +106,10 @@ export function VPExplorer(props: { return f; } function handleSetForm(form: T.FormVersion) { - setVps(o => ({ - ...o, - form, - })); + adjustVps({ + type: "set form", + payload: form, + }); } // for some crazy reason I can't get the URI share thing to encode and decode properly function handleCopyShareLink() { @@ -135,7 +123,7 @@ export function VPExplorer(props: { return
@@ -207,7 +195,7 @@ export function VPExplorer(props: {
@@ -237,13 +225,6 @@ export function VPExplorer(props: { export default VPExplorer; -function hasPronounConflict(subject: T.NPSelection | undefined, object: undefined | T.VerbObject): boolean { - const subjPronoun = (subject && subject.type === "pronoun") ? subject : undefined; - const objPronoun = (object && typeof object === "object" && object.type === "pronoun") ? object : undefined; - if (!subjPronoun || !objPronoun) return false; - return isInvalidSubjObjCombo(subjPronoun.person, objPronoun.person); -} - function getShareUrl(vps: T.VPSelectionState): string { const stringJSON = JSON.stringify(vps); const encoded = LZString.compressToEncodedURIComponent(stringJSON); diff --git a/src/components/vp-explorer/VerbPicker.tsx b/src/components/vp-explorer/VerbPicker.tsx index 15cd1f9..cbc92df 100644 --- a/src/components/vp-explorer/VerbPicker.tsx +++ b/src/components/vp-explorer/VerbPicker.tsx @@ -5,13 +5,15 @@ import { getVerbInfo } from "../../lib/verb-info"; import Hider from "../Hider"; import useStickyState from "../../lib/useStickyState"; import CompoundDisplay from "./CompoundDisplay"; -import { changeStatDyn, changeTransitivity, changeVoice } from "./verb-selection"; +import { + VpsReducerAction +} from "./vps-reducer"; // TODO: dark on past tense selecitons function VerbPicker(props: { vps: T.VPSelectionState, - onChange: (p: T.VPSelectionState) => void, + onChange: (a: VpsReducerAction) => void, opts: T.TextOptions, handleLinkClick: ((ts: number) => void) | "none", }) { @@ -28,42 +30,25 @@ function VerbPicker(props: { return
ERROR: Verb version should be select first
; } function onVoiceSelect(value: "active" | "passive") { - if (props.vps.verb && props.vps.verb.canChangeVoice) { - if (value === "passive" && props.vps.verb.tenseCategory === "imperative") { - return; - } - if (value === "passive" && (typeof props.vps.verb.object === "object")) { - props.onChange({ - ...props.vps, - subject: props.vps.verb.object, - verb: changeVoice(props.vps.verb, value, props.vps.verb.object), - }); - } else { - props.onChange({ - ...props.vps, - verb: changeVoice(props.vps.verb, value, value === "active" ? props.vps.subject : undefined), - }); - } - } + props.onChange({ + type: "set voice", + payload: value, + }); } function notInstransitive(t: "transitive" | "intransitive" | "grammatically transitive"): "transitive" | "grammatically transitive" { return t === "intransitive" ? "transitive" : t; } - function handleChangeTransitivity(t: "transitive" | "grammatically transitive") { - if (props.vps.verb && props.vps.verb.canChangeTransitivity) { - props.onChange({ - ...props.vps, - verb: changeTransitivity(props.vps.verb, t), - }); - } + function handleChangeTransitivity(payload: "transitive" | "grammatically transitive") { + props.onChange({ + type: "set transitivity", + payload, + }); } - function handleChangeStatDyn(c: "stative" | "dynamic") { - if (props.vps.verb && props.vps.verb.canChangeStatDyn) { - props.onChange({ - ...props.vps, - verb: changeStatDyn(props.vps.verb, c), - }); - } + function handleChangeStatDyn(payload: "stative" | "dynamic") { + props.onChange({ + type: "set statDyn", + payload, + }); } return
{info && , e?: string[] }; -export function compileEP(EP: T.EPRendered, form: T.FormVersion, combineLengths: true): { ps: T.PsString[], e?: string[] }; -export function compileEP(EP: T.EPRendered, form: T.FormVersion, combineLengths?: true): { ps: T.SingleOrLengthOpts, e?: string[] } { - const { kids, NPs } = getSegmentsAndKids(EP, form); +export function compileEP(EP: T.EPRendered): { ps: T.SingleOrLengthOpts, e?: string[] }; +export function compileEP(EP: T.EPRendered, combineLengths: true): { ps: T.PsString[], e?: string[] }; +export function compileEP(EP: T.EPRendered, combineLengths?: true): { ps: T.SingleOrLengthOpts, e?: string[] } { + const { kids, NPs } = getSegmentsAndKids(EP); const equative = EP.equative.ps; const psResult = compilePs({ NPs, @@ -35,15 +35,9 @@ export function compileEP(EP: T.EPRendered, form: T.FormVersion, combineLengths? }; } -function getSegmentsAndKids(EP: T.EPRendered, form: Omit): { kids: Segment[], NPs: Segment[] } { - function ifNotRemoved(s: Segment, role: "subject" | "predicate"): Segment[] { - if (form.removeKing && EP.king === role) { - return []; - } - return [s]; - } - const possToShrink = findPossesiveToShrink(EP); - const shrunkenPossAllowed = !(form.removeKing && possToShrink?.role === "king"); +function getSegmentsAndKids(EP: T.EPRendered): { kids: Segment[], NPs: Segment[] } { + const possToShrink = findPossesiveToShrinkInEP(EP); + const shrunkenPossAllowed = !((possToShrink?.from === "subject") && EP.omitSubject); const possUid = shrunkenPossAllowed ? EP.shrunkenPossesive : undefined; const subject = makeSegment(getPashtoFromRendered(EP.subject, possUid, false)); const predicate = makeSegment(getPashtoFromRendered(EP.predicate, possUid, false)); @@ -53,12 +47,12 @@ function getSegmentsAndKids(EP: T.EPRendered, form: Omit | undefined { - const uid = VP.shrunkenPossesive; - function findPossesiveInNP(NP: T.Rendered | T.ObjectNP | undefined): T.Rendered | undefined { - if (NP === undefined) return undefined; - if (typeof NP !== "object") return undefined; - if (!NP.possesor) return undefined; - if (NP.possesor.uid === uid) { - return NP.possesor.np; - } - return findPossesiveInNP(NP.possesor.np); +function findPossesiveInNP(NP: T.Rendered | T.ObjectNP | undefined, uid: number): T.Rendered | undefined { + if (NP === undefined) return undefined; + if (typeof NP !== "object") return undefined; + if (!NP.possesor) return undefined; + if (NP.possesor.uid === uid) { + return NP.possesor.np; } + return findPossesiveInNP(NP.possesor.np, uid); +} + +export function findPossesiveToShrinkInEP(EP: T.EPRendered): { + np: T.Rendered, + from: "subject" | "predicate", +} | undefined { + const uid = EP.shrunkenPossesive; if (uid === undefined) return undefined; - const objPred: T.Rendered | undefined = ("object" in VP) - ? (typeof VP.object === "object" ? VP.object : undefined) - : (VP.predicate.type === "noun" || VP.predicate.type === "participle" || VP.predicate.type === "pronoun") - // typescript is dumb here; - ? VP.predicate as T.Rendered + const inSubject = findPossesiveInNP(EP.subject, uid); + if (inSubject) { + return { + np: inSubject, + from: "subject", + }; + } + if (EP.predicate.type === "adjective" || EP.predicate.type === "loc. adv.") { + return undefined; + + } + // ts being stupid + const predicate = EP.predicate as T.Rendered; + const inPredicate = findPossesiveInNP(predicate, uid); + if (inPredicate) { + return { + np: inPredicate, + from: "predicate", + }; + } + return undefined; +} + +export function findPossesiveToShrinkInVP(VP: T.VPRendered): T.Rendered | undefined { + const uid = VP.shrunkenPossesive; + if (uid === undefined) return undefined; + const obj: T.Rendered | undefined = ("object" in VP && typeof VP.object === "object") + ? VP.object : undefined; return ( - findPossesiveInNP(VP.subject) + findPossesiveInNP(VP.subject, uid) || - findPossesiveInNP(objPred) + findPossesiveInNP(obj, uid) ); } diff --git a/src/lib/phrase-building/compile-vp.ts b/src/lib/phrase-building/compile-vp.ts index a03d72a..79985cb 100644 --- a/src/lib/phrase-building/compile-vp.ts +++ b/src/lib/phrase-building/compile-vp.ts @@ -22,7 +22,7 @@ import { isImperativeTense, isModalTense, isPerfectTense } from "../type-predica import { getEnglishFromRendered, getPashtoFromRendered } from "./np-tools"; import { orderKidsSection, - findPossesiveToShrink, + findPossesiveToShrinkInVP, shrinkNP, } from "./compile-tools"; @@ -93,7 +93,7 @@ function getSegmentsAndKids(VP: T.VPRendered, form: Form): { kids: Segment[], NP return servant; })(); const shrunkenServant = toShrinkServant ? shrinkNP(toShrinkServant) : undefined; - const possToShrink = findPossesiveToShrink(VP); + const possToShrink = findPossesiveToShrinkInVP(VP); const shrunkenPossesive = possToShrink ? shrinkNP(possToShrink) : undefined; const shrunkenPossAllowed = possToShrink && shrunkenPossesive && ( !shrunkenServant || !psStringEquals(shrunkenPossesive.ps[0], shrunkenServant.ps[0]) diff --git a/src/lib/phrase-building/render-ep.ts b/src/lib/phrase-building/render-ep.ts index 3949c09..ef80394 100644 --- a/src/lib/phrase-building/render-ep.ts +++ b/src/lib/phrase-building/render-ep.ts @@ -33,7 +33,7 @@ export function renderEP(EP: T.EPSelectionComplete): T.EPRendered { equative: renderEquative(EP.equative, kingPerson), englishBase: equativeBuilders[EP.equative.tense](kingPerson, EP.equative.negative), shrunkenPossesive: EP.shrunkenPossesive, - form: EP.form, + omitSubject: EP.omitSubject, }; } diff --git a/src/lib/useStickyState.ts b/src/lib/useStickyState.ts index 3c0e2da..a31e8aa 100644 --- a/src/lib/useStickyState.ts +++ b/src/lib/useStickyState.ts @@ -1,5 +1,7 @@ import { useEffect, useState } from "react"; +type SaveableData = string | number | object | boolean | undefined | null + /** * replacement from the React useState hook that will persist the state in local storage * @@ -8,7 +10,7 @@ import { useEffect, useState } from "react"; * @param key a key for saving the state in locolStorage * @returns */ -export default function useStickyState(defaultValue: T | ((old: T | undefined) => T), key: string): [ +export default function useStickyState(defaultValue: T | ((old: T | undefined) => T), key: string): [ value: T, setValue: React.Dispatch>, ] { @@ -42,3 +44,17 @@ export default function useStickyState( + reducer: (state: T, dispatch: A) => T, + defaultValue: T | ((old: T | undefined) => T), + key: string, +): [T, (action: A) => void] { + const [state, unsafeSetState] = useStickyState(defaultValue, key); + function adjustState(action: A) { + unsafeSetState(oldState => { + return reducer(oldState, action); + }); + } + return [state, adjustState]; +} diff --git a/src/types.ts b/src/types.ts index b0f5518..e7c81b1 100644 --- a/src/types.ts +++ b/src/types.ts @@ -667,7 +667,7 @@ export type EPSelectionState = { }, equative: EquativeSelection, shrunkenPossesive: undefined | number, - form: FormVersion, + omitSubject: boolean, }; export type EPSelectionComplete = Omit & { @@ -679,7 +679,7 @@ export type EPSelectionComplete = Omit, - predicate: Rendered, + predicate: Rendered | Rendered, equative: EquativeRendered, englishBase?: string[], shrunkenPossesive: undefined | number, - form: FormVersion, + omitSubject: boolean, } export type EntryFeeder = {