mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-05 19:28:39 +00:00
* Implement strict mode (replacing unicodeTextInMathMode) Add new "strict" setting (default value false) that can take a boolean (whether to throw an error or silently ignore), string ("ignore", "warn", or "error"), or a function possibly returning such a value. This enables a variety of ways of handling or ignoring transgressions from "true" LaTeX behavior, making KaTeX easier to use while still providing the ability for strict LaTeX adherance. Resolve #1226, implementing that spec, for two existing transgressions from regular LaTeX: * src/functions/kern.js had some errors and warnings about use of (units in) math vs. text mode commands. * The former setting unicodeTextInMathMode (not in any released version) needed to be set to true to enable Unicode text symbols in math mode. Now these are controlled by the strict setting. By default, KaTeX is now very permissive, but if desired, the user can request warnings or errors. * Rewrite strict description * Add tests for strict functions * Stricter type for strict * Switch default strict setting to "warn" * Fix new flow error * Fix another flow bug
183 lines
6.4 KiB
JavaScript
183 lines
6.4 KiB
JavaScript
/* eslint max-len:0 */
|
|
/* global beforeEach: false */
|
|
/* global expect: false */
|
|
/* global it: false */
|
|
/* global describe: false */
|
|
import ParseError from "../src/ParseError";
|
|
import parseTree from "../src/parseTree";
|
|
import Settings from "../src/Settings";
|
|
import {scriptFromCodepoint, supportedCodepoint} from "../src/unicodeScripts";
|
|
|
|
const defaultSettings = new Settings({
|
|
strict: false, // deal with warnings only when desired
|
|
});
|
|
const strictSettings = new Settings({strict: true});
|
|
|
|
const parseAndSetResult = function(expr, result, settings) {
|
|
try {
|
|
return parseTree(expr, settings || defaultSettings);
|
|
} catch (e) {
|
|
result.pass = false;
|
|
if (e instanceof ParseError) {
|
|
result.message = () => "'" + expr + "' failed " +
|
|
"parsing with error: " + e.message;
|
|
} else {
|
|
result.message = () => "'" + expr + "' failed " +
|
|
"parsing with unknown error: " + e.message;
|
|
}
|
|
}
|
|
};
|
|
|
|
describe("unicode", function() {
|
|
beforeEach(function() {
|
|
expect.extend({
|
|
|
|
toParse: function(actual, settings) {
|
|
const usedSettings = settings ? settings : defaultSettings;
|
|
|
|
const result = {
|
|
pass: true,
|
|
message: () => "'" + actual + "' succeeded parsing",
|
|
};
|
|
parseAndSetResult(actual, result, usedSettings);
|
|
return result;
|
|
},
|
|
|
|
toNotParse: function(actual, settings) {
|
|
const usedSettings = settings ? settings : defaultSettings;
|
|
|
|
const result = {
|
|
pass: false,
|
|
message: () => "Expected '" + actual + "' to fail " +
|
|
"parsing, but it succeeded",
|
|
};
|
|
|
|
try {
|
|
parseTree(actual, usedSettings);
|
|
} catch (e) {
|
|
if (e instanceof ParseError) {
|
|
result.pass = true;
|
|
result.message = () => "'" + actual + "' correctly " +
|
|
"didn't parse with error: " + e.message;
|
|
} else {
|
|
result.message = () => "'" + actual + "' failed " +
|
|
"parsing with unknown error: " + e.message;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
});
|
|
});
|
|
|
|
it("should parse Latin-1 inside \\text{}", function() {
|
|
expect('\\text{ÀÁÂÃÄÅÈÉÊËÌÍÎÏÑÒÓÔÕÖÙÚÛÜÝàáâãäåèéêëìíîïñòóôõöùúûüýÿ' +
|
|
'ÆÇÐØÞßæçðøþ}').toParse();
|
|
});
|
|
|
|
it("should not parse Latin-1 outside \\text{} with strict", function() {
|
|
const chars = 'ÀÁÂÃÄÅÈÉÊËÌÍÎÏÑÒÓÔÕÖÙÚÛÜÝàáâãäåèéêëìíîïñòóôõöùúûüýÿÇÐÞçþ';
|
|
for (const ch of chars) {
|
|
expect(ch).toNotParse(strictSettings);
|
|
}
|
|
});
|
|
|
|
it("should parse Latin-1 outside \\text{}", function() {
|
|
expect('ÀÁÂÃÄÅÈÉÊËÌÍÎÏÑÒÓÔÕÖÙÚÛÜÝàáâãäåèéêëìíîïñòóôõöùúûüýÿ' +
|
|
'ÇÐÞçðþ').toParse();
|
|
});
|
|
|
|
it("should parse all lower case Greek letters", function() {
|
|
expect("αβγδεϵζηθϑικλμνξοπϖρϱςστυφϕχψω").toParse();
|
|
});
|
|
|
|
it("should parse math upper case Greek letters", function() {
|
|
expect("ΓΔΘΛΞΠΣΥΦΨΩ").toParse();
|
|
});
|
|
|
|
it("should parse Cyrillic inside \\text{}", function() {
|
|
expect('\\text{БГДЖЗЙЛФЦШЫЮЯ}').toParse();
|
|
});
|
|
|
|
it("should not parse Cyrillic outside \\text{} with strict", function() {
|
|
expect('БГДЖЗЙЛФЦШЫЮЯ').toNotParse(strictSettings);
|
|
});
|
|
|
|
it("should parse CJK inside \\text{}", function() {
|
|
expect('\\text{私はバナナです}').toParse();
|
|
expect('\\text{여보세요}').toParse();
|
|
});
|
|
|
|
it("should not parse CJK outside \\text{} with strict", function() {
|
|
expect('私はバナナです。').toNotParse(strictSettings);
|
|
expect('여보세요').toNotParse(strictSettings);
|
|
});
|
|
|
|
it("should parse Devangari inside \\text{}", function() {
|
|
expect('\\text{नमस्ते}').toParse();
|
|
});
|
|
|
|
it("should not parse Devangari outside \\text{} with strict", function() {
|
|
expect('नमस्ते').toNotParse(strictSettings);
|
|
});
|
|
|
|
it("should parse Georgian inside \\text{}", function() {
|
|
expect('\\text{გამარჯობა}').toParse();
|
|
});
|
|
|
|
it("should not parse Georgian outside \\text{} with strict", function() {
|
|
expect('გამარჯობა').toNotParse(strictSettings);
|
|
});
|
|
|
|
it("should parse extended Latin characters inside \\text{}", function() {
|
|
expect('\\text{ěščřžůřťďňőİı}').toParse();
|
|
});
|
|
|
|
it("should not parse extended Latin outside \\text{} with strict", function() {
|
|
expect('ěščřžůřťďňőİı').toNotParse(strictSettings);
|
|
});
|
|
|
|
});
|
|
|
|
describe("unicodeScripts", () => {
|
|
const scriptRegExps = {
|
|
latin: /[\u0100-\u024f\u0300-\u036f]/,
|
|
cyrillic: /[\u0400-\u04ff]/,
|
|
brahmic: /[\u0900-\u109F]/,
|
|
georgian: /[\u10a0-\u10ff]/,
|
|
cjk: /[\u3000-\u30FF\u4E00-\u9FAF\uFF00-\uFF60]/,
|
|
hangul: /[\uAC00-\uD7AF]/,
|
|
};
|
|
|
|
const scriptNames = Object.keys(scriptRegExps);
|
|
|
|
const allRegExp = new RegExp(
|
|
Object.values(scriptRegExps).map(re => re.source).join('|')
|
|
);
|
|
|
|
it("supportedCodepoint() should return the correct values", () => {
|
|
for (let codepoint = 0; codepoint <= 0xffff; codepoint++) {
|
|
expect(supportedCodepoint(codepoint)).toBe(
|
|
allRegExp.test(String.fromCharCode(codepoint))
|
|
);
|
|
}
|
|
});
|
|
|
|
it("scriptFromCodepoint() should return correct values", () => {
|
|
outer: for (let codepoint = 0; codepoint <= 0xffff; codepoint++) {
|
|
const character = String.fromCharCode(codepoint);
|
|
const script = scriptFromCodepoint(codepoint);
|
|
|
|
for (const scriptName of scriptNames) {
|
|
if (scriptRegExps[scriptName].test(character)) {
|
|
expect(script).toEqual(scriptName);
|
|
continue outer;
|
|
}
|
|
}
|
|
|
|
expect(script).toBe(null);
|
|
expect(supportedCodepoint(codepoint)).toBe(false);
|
|
}
|
|
});
|
|
});
|