mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-09-22 20:24:18 -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 chunks = path.split(/(?<!`)\//).filter((s) => !!s);
|
||||
const chunks = path.split(/(?<!`)\//);
|
||||
let plural = false;
|
||||
let pluralHonorific = false;
|
||||
let description = '';
|
||||
@ -77,7 +77,13 @@ const buildPronounFromSlashes = (config, path) => {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
morphemeChunks.push(unescapeControlSymbols(chunk));
|
||||
if (chunk === '~') {
|
||||
morphemeChunks.push(null);
|
||||
} else if (chunk === ' ') {
|
||||
morphemeChunks.push('');
|
||||
} else {
|
||||
morphemeChunks.push(unescapeControlSymbols(chunk));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (description.length > Pronoun.DESCRIPTION_MAXLENGTH) {
|
||||
|
@ -478,7 +478,16 @@ export class Pronoun {
|
||||
} else {
|
||||
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]) {
|
||||
chunks.push(':plural');
|
||||
@ -490,7 +499,8 @@ export class Pronoun {
|
||||
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) {
|
||||
|
@ -269,12 +269,18 @@ const escapeChars = {
|
||||
export const escapeHtml = (text) => text.replace(/[&<>"]/g, (tag) => escapeChars[tag] || tag);
|
||||
|
||||
export const escapeControlSymbols = (text) => {
|
||||
if (text === null) {
|
||||
return null;
|
||||
}
|
||||
// a backtick is used because browsers replace backslashes
|
||||
// with forward slashes if they are not url-encoded
|
||||
return text.replaceAll(/[`&/|,]/g, '`$&');
|
||||
};
|
||||
|
||||
export const unescapeControlSymbols = (text) => {
|
||||
if (text === null) {
|
||||
return null;
|
||||
}
|
||||
return text.replaceAll(/`(.)/g, '$1');
|
||||
};
|
||||
|
||||
|
@ -162,6 +162,21 @@ describe('when configured that slashes contain all morphemes', () => {
|
||||
actual.toBeDefined();
|
||||
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', () => {
|
||||
expect(buildPronoun(pronouns, 'ae/aer/aer/aerself')).toBeUndefined();
|
||||
});
|
||||
|
@ -56,6 +56,18 @@ describe('formatting a pronoun with slashes', () => {
|
||||
expect(generatedPronouns.sSlashHe.toStringSlashes())
|
||||
.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', () => {
|
||||
expect(generatedPronouns.aerPlural.toStringSlashes())
|
||||
.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__',
|
||||
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(
|
||||
'ae/aer',
|
||||
'',
|
||||
@ -207,6 +224,7 @@ export const generated = {
|
||||
aerWithDescription,
|
||||
aerPluralWithDescription,
|
||||
aerWithEmptyPossessivePronoun,
|
||||
aerWithEmptyReflexive,
|
||||
aerWithUnsetPossessivePronoun,
|
||||
sSlashHe,
|
||||
};
|
||||
|
@ -36,12 +36,18 @@ const controlSymbols = [
|
||||
];
|
||||
|
||||
describe('when escaping control symbols', () => {
|
||||
test('safely handles null', () => {
|
||||
expect(escapeControlSymbols(null)).toBeNull();
|
||||
});
|
||||
test.each(controlSymbols)('$description get escaped with `', ({ unescaped, escaped }) => {
|
||||
expect(escapeControlSymbols(unescaped)).toBe(escaped);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when unescaping control symbols', () => {
|
||||
test('safely handles null', () => {
|
||||
expect(unescapeControlSymbols(null)).toBeNull();
|
||||
});
|
||||
test.each(controlSymbols)('$description get unescaped', ({ unescaped, escaped }) => {
|
||||
expect(unescapeControlSymbols(escaped)).toBe(unescaped);
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user