mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-09-23 04:34:15 -04:00
(pronouns) add support for empty and unset morphs in slash notation
This commit is contained in:
parent
48992c53d6
commit
6309daa78c
@ -59,7 +59,7 @@ const buildPronounFromTemplate = (key, template) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const buildPronounFromSlashes = (config, path) => {
|
const buildPronounFromSlashes = (config, path) => {
|
||||||
const chunks = path.split(/(?<!`)\//).filter((s) => !!s);
|
const chunks = path.split(/(?<!`)\//);
|
||||||
let plural = false;
|
let plural = false;
|
||||||
let pluralHonorific = false;
|
let pluralHonorific = false;
|
||||||
let description = '';
|
let description = '';
|
||||||
@ -76,10 +76,16 @@ const buildPronounFromSlashes = (config, path) => {
|
|||||||
description = unescapeControlSymbols(chunk.substring(descriptionModifierPrefix.length));
|
description = unescapeControlSymbols(chunk.substring(descriptionModifierPrefix.length));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (chunk === '~') {
|
||||||
|
morphemeChunks.push(null);
|
||||||
|
} else if (chunk === ' ') {
|
||||||
|
morphemeChunks.push('');
|
||||||
} else {
|
} else {
|
||||||
morphemeChunks.push(unescapeControlSymbols(chunk));
|
morphemeChunks.push(unescapeControlSymbols(chunk));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (description.length > Pronoun.DESCRIPTION_MAXLENGTH) {
|
if (description.length > Pronoun.DESCRIPTION_MAXLENGTH) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -478,7 +478,16 @@ export class Pronoun {
|
|||||||
} else {
|
} else {
|
||||||
chunks = Object.values(this.morphemes);
|
chunks = Object.values(this.morphemes);
|
||||||
}
|
}
|
||||||
chunks = chunks.map(escapeControlSymbols);
|
chunks = chunks.map((chunk) => {
|
||||||
|
if (chunk === null) {
|
||||||
|
return '~';
|
||||||
|
} else if (chunk === '') {
|
||||||
|
// use an extra space because double slashes get replaced by a single one during a request
|
||||||
|
return ' ';
|
||||||
|
} else {
|
||||||
|
return escapeControlSymbols(chunk);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (this.plural[0]) {
|
if (this.plural[0]) {
|
||||||
chunks.push(':plural');
|
chunks.push(':plural');
|
||||||
@ -490,7 +499,8 @@ export class Pronoun {
|
|||||||
chunks.push(`:description=${escapeControlSymbols(this.description)}`);
|
chunks.push(`:description=${escapeControlSymbols(this.description)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return chunks.join('/');
|
// encode a trailing space so that it does not get removed during a request
|
||||||
|
return chunks.join('/').replace(/ $/, encodeURI(' '));
|
||||||
}
|
}
|
||||||
|
|
||||||
static from(data) {
|
static from(data) {
|
||||||
|
@ -269,12 +269,18 @@ const escapeChars = {
|
|||||||
export const escapeHtml = (text) => text.replace(/[&<>"]/g, (tag) => escapeChars[tag] || tag);
|
export const escapeHtml = (text) => text.replace(/[&<>"]/g, (tag) => escapeChars[tag] || tag);
|
||||||
|
|
||||||
export const escapeControlSymbols = (text) => {
|
export const escapeControlSymbols = (text) => {
|
||||||
|
if (text === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
// a backtick is used because browsers replace backslashes
|
// a backtick is used because browsers replace backslashes
|
||||||
// with forward slashes if they are not url-encoded
|
// with forward slashes if they are not url-encoded
|
||||||
return text.replaceAll(/[`&/|,]/g, '`$&');
|
return text.replaceAll(/[`&/|,]/g, '`$&');
|
||||||
};
|
};
|
||||||
|
|
||||||
export const unescapeControlSymbols = (text) => {
|
export const unescapeControlSymbols = (text) => {
|
||||||
|
if (text === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return text.replaceAll(/`(.)/g, '$1');
|
return text.replaceAll(/`(.)/g, '$1');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -162,6 +162,21 @@ describe('when configured that slashes contain all morphemes', () => {
|
|||||||
actual.toBeDefined();
|
actual.toBeDefined();
|
||||||
actual.toEqual(generatedPronouns.aerPluralWithDescription);
|
actual.toEqual(generatedPronouns.aerPluralWithDescription);
|
||||||
});
|
});
|
||||||
|
test('builds generated pronoun with some morphemes empty', () => {
|
||||||
|
const actual = expect(buildPronoun(pronouns, 'ae/aer/aer/ /aerself'));
|
||||||
|
actual.toBeDefined();
|
||||||
|
actual.toEqual(generatedPronouns.aerWithEmptyPossessivePronoun);
|
||||||
|
});
|
||||||
|
test('builds generated pronoun with morpheme at end empty', () => {
|
||||||
|
const actual = expect(buildPronoun(pronouns, 'ae/aer/aer/aers/'));
|
||||||
|
actual.toBeDefined();
|
||||||
|
actual.toEqual(generatedPronouns.aerWithEmptyReflexive);
|
||||||
|
});
|
||||||
|
test('builds generated pronoun with some morphemes unset', () => {
|
||||||
|
const actual = expect(buildPronoun(pronouns, 'ae/aer/aer/~/aerself'));
|
||||||
|
actual.toBeDefined();
|
||||||
|
actual.toEqual(generatedPronouns.aerWithUnsetPossessivePronoun);
|
||||||
|
});
|
||||||
test('builds nothing if morphemes are missing', () => {
|
test('builds nothing if morphemes are missing', () => {
|
||||||
expect(buildPronoun(pronouns, 'ae/aer/aer/aerself')).toBeUndefined();
|
expect(buildPronoun(pronouns, 'ae/aer/aer/aerself')).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
@ -56,6 +56,18 @@ describe('formatting a pronoun with slashes', () => {
|
|||||||
expect(generatedPronouns.sSlashHe.toStringSlashes())
|
expect(generatedPronouns.sSlashHe.toStringSlashes())
|
||||||
.toEqual('s`/he/hir/hir/hirs/hirself');
|
.toEqual('s`/he/hir/hir/hirs/hirself');
|
||||||
});
|
});
|
||||||
|
test('empty morphemes receive space as placeholder', () => {
|
||||||
|
expect(generatedPronouns.aerWithEmptyPossessivePronoun.toStringSlashes())
|
||||||
|
.toEqual('ae/aer/aer/ /aerself');
|
||||||
|
});
|
||||||
|
test('empty morphemes at end receive url-encoded space as placeholder', () => {
|
||||||
|
expect(generatedPronouns.aerWithEmptyReflexive.toStringSlashes())
|
||||||
|
.toEqual('ae/aer/aer/aers/%20');
|
||||||
|
});
|
||||||
|
test('unset morphemes receive tilde as placeholder', () => {
|
||||||
|
expect(generatedPronouns.aerWithUnsetPossessivePronoun.toStringSlashes())
|
||||||
|
.toEqual('ae/aer/aer/~/aerself');
|
||||||
|
});
|
||||||
test('adds plural modifier if necessary', () => {
|
test('adds plural modifier if necessary', () => {
|
||||||
expect(generatedPronouns.aerPlural.toStringSlashes())
|
expect(generatedPronouns.aerPlural.toStringSlashes())
|
||||||
.toEqual('ae/aer/aer/aers/aerselves/:plural');
|
.toEqual('ae/aer/aer/aers/aerselves/:plural');
|
||||||
|
18
test/fixtures/pronouns.js
vendored
18
test/fixtures/pronouns.js
vendored
@ -164,6 +164,23 @@ const aerWithEmptyPossessivePronoun = new Pronoun(
|
|||||||
'__generator__',
|
'__generator__',
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
const aerWithEmptyReflexive = new Pronoun(
|
||||||
|
'ae/aer',
|
||||||
|
'',
|
||||||
|
false,
|
||||||
|
{
|
||||||
|
pronoun_subject: 'ae',
|
||||||
|
pronoun_object: 'aer',
|
||||||
|
possessive_determiner: 'aer',
|
||||||
|
possessive_pronoun: 'aers',
|
||||||
|
reflexive: '',
|
||||||
|
},
|
||||||
|
[false],
|
||||||
|
[false],
|
||||||
|
[],
|
||||||
|
'__generator__',
|
||||||
|
false,
|
||||||
|
);
|
||||||
const aerWithUnsetPossessivePronoun = new Pronoun(
|
const aerWithUnsetPossessivePronoun = new Pronoun(
|
||||||
'ae/aer',
|
'ae/aer',
|
||||||
'',
|
'',
|
||||||
@ -207,6 +224,7 @@ export const generated = {
|
|||||||
aerWithDescription,
|
aerWithDescription,
|
||||||
aerPluralWithDescription,
|
aerPluralWithDescription,
|
||||||
aerWithEmptyPossessivePronoun,
|
aerWithEmptyPossessivePronoun,
|
||||||
|
aerWithEmptyReflexive,
|
||||||
aerWithUnsetPossessivePronoun,
|
aerWithUnsetPossessivePronoun,
|
||||||
sSlashHe,
|
sSlashHe,
|
||||||
};
|
};
|
||||||
|
@ -36,12 +36,18 @@ const controlSymbols = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
describe('when escaping control symbols', () => {
|
describe('when escaping control symbols', () => {
|
||||||
|
test('safely handles null', () => {
|
||||||
|
expect(escapeControlSymbols(null)).toBeNull();
|
||||||
|
});
|
||||||
test.each(controlSymbols)('$description get escaped with `', ({ unescaped, escaped }) => {
|
test.each(controlSymbols)('$description get escaped with `', ({ unescaped, escaped }) => {
|
||||||
expect(escapeControlSymbols(unescaped)).toBe(escaped);
|
expect(escapeControlSymbols(unescaped)).toBe(escaped);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when unescaping control symbols', () => {
|
describe('when unescaping control symbols', () => {
|
||||||
|
test('safely handles null', () => {
|
||||||
|
expect(unescapeControlSymbols(null)).toBeNull();
|
||||||
|
});
|
||||||
test.each(controlSymbols)('$description get unescaped', ({ unescaped, escaped }) => {
|
test.each(controlSymbols)('$description get unescaped', ({ unescaped, escaped }) => {
|
||||||
expect(unescapeControlSymbols(escaped)).toBe(unescaped);
|
expect(unescapeControlSymbols(escaped)).toBe(unescaped);
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user