Skip to content

Commit

Permalink
Merge pull request #275 from desmosinc/fix-binom
Browse files Browse the repository at this point in the history
Fix \binom and \choose
  • Loading branch information
jared-hughes authored Feb 29, 2024
2 parents b47f030 + e35463c commit 43f0c60
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 2 deletions.
24 changes: 22 additions & 2 deletions src/commands/math/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -939,7 +939,12 @@ var LiveFraction =
if (!this.replacedFragment) {
var leftward = cursor[L];

if (!cursor.options.typingSlashCreatesNewFraction) {
const dontScan =
cursor.options.typingSlashCreatesNewFraction &&
this instanceof Fraction;

if (!dontScan) {
// The user is typing "/" or "over" or "choose". Scan left to get content inside it.
while (
leftward &&
!(
Expand All @@ -956,6 +961,8 @@ var LiveFraction =
leftward instanceof SummationNotation &&
leftward[R] instanceof SupSub
) {
// The previous step scanned too far. `\sum_1^5` looks like [SummationNotation,SupSub],
// so scan back right
leftward = leftward[R] as MQNode;
let leftwardR = leftward[R];
if (
Expand All @@ -965,6 +972,9 @@ var LiveFraction =
leftward = leftward[R];
}

// `leftward` is the first node (from-right-to-left) that is broken on, so
// `leftwardR` is the last node (from right-to-left) that should be included in the
// top of the Fraction or Binomial.
if (leftward !== cursor[L] && !cursor.isTooDeep(1)) {
let leftwardR = (leftward as MQNode)[R] as MQNode;
let cursorL = cursor[L] as MQNode;
Expand Down Expand Up @@ -1677,13 +1687,23 @@ class Binomial extends DelimsNode {
textTemplate = ['choose(', ',', ')'];
mathspeakTemplate = ['StartBinomial,', 'Choose', ', EndBinomial'];
ariaLabel = 'binomial';

finalizeTree() {
const endsL = this.getEnd(L);
const endsR = this.getEnd(R);
this.upInto = endsR.upOutOf = endsL;
this.downInto = endsL.downOutOf = endsR;
// https://math.stackexchange.com/a/1617456 cites Knuth as the source of 'upper index' and 'lower index'
endsL.ariaLabel = 'upper index';
endsR.ariaLabel = 'lower index';
}
}

LatexCmds.binom = LatexCmds.binomial = Binomial;

LatexCmds.choose = class extends Binomial {
createLeftOf(cursor: Cursor) {
LiveFraction.prototype.createLeftOf(cursor);
LiveFraction.prototype.createLeftOf.call(this, cursor);
}
};

Expand Down
35 changes: 35 additions & 0 deletions test/unit/aria.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,41 @@ suite('aria', function () {
mathField.latex('');
});

test('typing and backspacing a binomial', function () {
mathField.typedText('1');
assertAriaEqual('1');
mathField.cmd('\\choose');
// Matching behavior of "over", we don't get "choose" as the ARIA here.
mathField.typedText('2');
assertAriaEqual('2');

mathField.keystroke('Tab');
assertAriaEqual('after StartBinomial, 1 Choose 2 , EndBinomial');

mathField.keystroke('Backspace');
assertAriaEqual('end of lower index 2');
mathField.keystroke('Backspace');
assertAriaEqual('2');
mathField.keystroke('Backspace');
assertAriaEqual('Choose');
mathField.keystroke('Backspace');
assertAriaEqual('1');
});

test('navigating a binomial', function () {
mathField.typedText('1');
assertAriaEqual('1');
mathField.cmd('\\choose');
// Matching behavior of "over", we don't get "choose" as the ARIA here.
mathField.typedText('2');
assertAriaEqual('2');
mathField.keystroke('Up');
assertAriaEqual('upper index 1');
mathField.keystroke('Down');
assertAriaEqual('lower index 2');
mathField.latex('');
});

test('typing and backspacing through parenthesies', function () {
mathField.typedText('(');
assertAriaEqual('left parenthesis');
Expand Down
23 changes: 23 additions & 0 deletions test/unit/typing.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,24 @@ suite('typing with auto-replaces', function () {
});
});

suite('Choose', function () {
test('full MathQuill', function () {
mq.typedText('1').cmd('\\choose').typedText('2').keystroke('Tab');
mq.typedText('+sinx').cmd('\\choose');
assertLatex('\\binom{1}{2}+\\binom{\\sin x}{ }');
mq.latex('').typedText('1+').cmd('\\choose').typedText('2');
assertLatex('1+\\binom{2}{ }');
mq.latex('').typedText('1 2').cmd('\\choose').typedText('3');
assertLatex('1\\ \\binom{2}{3}');
});

test('mathquill-basic', function () {
var mq_basic = MQBasic.MathField($('<span></span>').appendTo('#mock')[0]);
mq_basic.typedText('1').cmd('\\choose').typedText('2');
assert.equal(mq_basic.latex(), '\\binom{1}{2}');
});
});

suite('EquivalentMinus', function () {
test('different minus symbols', function () {
//these 4 are all different characters (!!)
Expand Down Expand Up @@ -1201,6 +1219,11 @@ suite('typing with auto-replaces', function () {
mq.typedText('1/');
assertLatex('1\\frac{ }{ }');
});

test("typing slash creates new fraction doesn't affect choose", function () {
mq.typedText('1').cmd('\\choose');
assertLatex('\\binom{1}{ }');
});
});

suite('autoCommands', function () {
Expand Down
71 changes: 71 additions & 0 deletions test/unit/updown.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,77 @@ suite('up/down', function () {
);
});

test('up/down into and within binomial', function () {
controller.renderLatexMath('\\binom{12}{34}');
var binom = rootBlock.ends[L],
numer = binom.ends[L],
denom = binom.ends[R];
assert.equal(binom.latex(), '\\binom{12}{34}', 'binomial is in root block');
assert.equal(
binom,
rootBlock.ends[R],
'binomial is sole child of root block'
);
assert.equal(
numer.latex(),
'12',
'numerator is left end child of binomial'
);
assert.equal(
denom.latex(),
'34',
'denominator is right end child of binomial'
);

mq.keystroke('Up');
assert.equal(cursor.parent, numer, 'cursor up goes into numerator');
assert.equal(
cursor[R],
0,
'cursor up from right of binomial inserts at right end of numerator'
);

mq.keystroke('Down');
assert.equal(cursor.parent, denom, 'cursor down goes into denominator');
assert.equal(
cursor[R],
0,
'cursor down from numerator inserts at right end of denominator'
);

mq.keystroke('Up');
assert.equal(cursor.parent, numer, 'cursor up goes into numerator');
assert.equal(
cursor[R],
0,
'cursor up from denominator inserts at right end of numerator'
);

mq.keystroke('Left Left Left');
assert.equal(cursor.parent, rootBlock, 'cursor outside binomial');
assert.equal(cursor[R], binom, 'cursor before binomial');

mq.keystroke('Up');
assert.equal(cursor.parent, numer, 'cursor up goes into numerator');
assert.equal(
cursor[L],
0,
'cursor up from left of binomial inserts at left end of numerator'
);

mq.keystroke('Left');
assert.equal(cursor.parent, rootBlock, 'cursor outside binomial');
assert.equal(cursor[R], binom, 'cursor before binomial');

mq.keystroke('Down');
assert.equal(cursor.parent, denom, 'cursor down goes into denominator');
assert.equal(
cursor[L],
0,
'cursor down from left of binomial inserts at left end of denominator'
);
});

test('nested subscripts and fractions', function () {
controller.renderLatexMath(
'\\frac{d}{dx_{\\frac{24}{36}0}}\\sqrt{x}=x^{\\frac{1}{2}}'
Expand Down

0 comments on commit 43f0c60

Please sign in to comment.