Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set TeX class OP for multi-letter mo elements, as in v2. (mathjax/MathJax#3095) #998

Merged
merged 1 commit into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 40 additions & 30 deletions ts/core/MmlTree/MmlNodes/mo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {PropertyList} from '../../Tree/Node.js';
import {AbstractMmlTokenNode, MmlNode, AttributeList, TEXCLASS} from '../MmlNode.js';
import {MmlMrow} from './mrow.js';
import {MmlMover, MmlMunder, MmlMunderover} from './munderover.js';
import {OperatorList, OPTABLE, getRange, MMLSPACING} from '../OperatorDictionary.js';
import {OperatorList, OPTABLE, OPDEF, getRange, MMLSPACING} from '../OperatorDictionary.js';
import {unicodeChars, unicodeString} from '../../../util/string.js';

/*****************************************************************/
Expand Down Expand Up @@ -101,6 +101,11 @@ export class MmlMo extends AbstractMmlTokenNode {
']+$'
].join(''));

/**
* Pattern to use to identify a multiletter operator
*/
protected static opPattern = /^[a-zA-Z]{2,}$/;

/**
* Default map for remapping prime characters
*/
Expand Down Expand Up @@ -150,11 +155,7 @@ export class MmlMo extends AbstractMmlTokenNode {
*/
public get texClass() {
if (this._texClass === null) {
let mo = this.getText();
let [form1, form2, form3] = this.handleExplicitForm(this.getForms());
let OPTABLE = (this.constructor as typeof MmlMo).OPTABLE;
let def = OPTABLE[form1][mo] || OPTABLE[form2][mo] || OPTABLE[form3][mo];
return def ? def[2] : TEXCLASS.REL;
return this.getOperatorDef(this.getText())[2];
}
return this._texClass;
}
Expand Down Expand Up @@ -348,35 +349,44 @@ export class MmlMo extends AbstractMmlTokenNode {
}

/**
* Set the attributes from the operator table
* get the operator definition from the operator table
*
* @param {string} mo The test of the mo element
* @param {string} mo The text of the mo element
*/
protected checkOperatorTable(mo: string) {
let [form1, form2, form3] = this.handleExplicitForm(this.getForms());
protected getOperatorDef(mo: string) {
const [form1, form2, form3] = this.handleExplicitForm(this.getForms());
this.attributes.setInherited('form', form1);
let OPTABLE = (this.constructor as typeof MmlMo).OPTABLE;
let def = OPTABLE[form1][mo] || OPTABLE[form2][mo] || OPTABLE[form3][mo];
const CLASS = this.constructor as typeof MmlMo
const OPTABLE = CLASS.OPTABLE;
const def = OPTABLE[form1][mo] || OPTABLE[form2][mo] || OPTABLE[form3][mo];
if (def) {
if (this.getProperty('texClass') === undefined) {
this.texClass = def[2];
}
for (const name of Object.keys(def[3] || {})) {
this.attributes.setInherited(name, def[3][name]);
}
this.lspace = (def[0] + 1) / 18;
this.rspace = (def[1] + 1) / 18;
} else {
let range = getRange(mo);
if (range) {
if (this.getProperty('texClass') === undefined) {
this.texClass = range[2];
}
const spacing = (this.constructor as typeof MmlMo).MMLSPACING[range[2]];
this.lspace = (spacing[0] + 1) / 18;
this.rspace = (spacing[1] + 1) / 18;
}
return def;
}
const limits = this.attributes.get('movablelimits');
const isOP = !!mo.match(CLASS.opPattern);
if ((isOP || limits) && this.getProperty('texClass') === undefined) {
return OPDEF(1, 2, TEXCLASS.OP);
}
const range = getRange(mo);
const [l, r] = CLASS.MMLSPACING[range[2]];
return OPDEF(l, r, range[2]);
}

/**
* Set the attributes from the operator table
*
* @param {string} mo The text of the mo element
*/
protected checkOperatorTable(mo: string) {
const def = this.getOperatorDef(mo);
if (this.getProperty('texClass') === undefined) {
this.texClass = def[2];
}
for (const name of Object.keys(def[3] || {})) {
this.attributes.setInherited(name, def[3][name]);
}
this.lspace = (def[0] + 1) / 18;
this.rspace = (def[1] + 1) / 18;
}

/**
Expand Down
8 changes: 4 additions & 4 deletions ts/core/MmlTree/OperatorDictionary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,10 @@ export const RANGES: RangeDef[] = [
/**
* Get the Unicode range for the first character of a string
*
* @param {string} text The character to check
* @return {RangeDef|null} The range containing that character, or null
* @param {string} text The character to check
* @return {RangeDef} The range containing that character, or null
*/
export function getRange(text: string): RangeDef | null {
export function getRange(text: string): RangeDef {
const n = text.codePointAt(0);
for (const range of RANGES) {
if (n <= range[1]) {
Expand All @@ -149,7 +149,7 @@ export function getRange(text: string): RangeDef | null {
break;
}
}
return null;
return [0, 0, TEXCLASS.REL, 'mo'];
}

/**
Expand Down
4 changes: 2 additions & 2 deletions ts/input/tex/base/BaseConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ export function Other(parser: TexParser, char: string) {
{mathvariant: parser.stack.env['font']} : {};
const remap = (MapHandler.getMap('remap') as CharacterMap).lookup(char);
const range = getRange(char);
const type = (range ? range[3] : 'mo');
const type = range[3]
// @test Other
// @test Other Remap
let mo = parser.create('token', type, def, (remap ? remap.char : char));
const variant = (range?.[4] ||
const variant = (range[4] ||
(ParseUtil.isLatinOrGreekChar(char) ? parser.configuration.mathStyle(char, true) : ''));
if (variant) {
mo.attributes.set('mathvariant', variant);
Expand Down