Skip to content
Open
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
/node_modules
/build

.idea/
.vscode/

53 changes: 53 additions & 0 deletions src/commands/math/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1325,6 +1325,8 @@ class Bracket extends DelimsNode {
[L]: { ch: string; ctrlSeq: string };
[R]: { ch: string; ctrlSeq: string };
};
// Skip mathspeak and instead emit an empty string.
skipMathspeak = false;
constructor(
side: BracketSide,
open: string,
Expand Down Expand Up @@ -1404,6 +1406,7 @@ class Bracket extends DelimsNode {
this.checkCursorContextClose(ctx);
}
mathspeak(opts?: MathspeakOptions) {
if (this.skipMathspeak) return '';
var open = this.sides[L].ch,
close = this.sides[R].ch;
if (open === '|' && close === '|') {
Expand Down Expand Up @@ -1454,7 +1457,57 @@ class Bracket extends DelimsNode {
.removeClass('mq-ghost');
this.replaceBracket(brackFrag, this.side);
}

// Returns true iff handled the insertion.
unwrapSelectedBracket(cursor: Cursor) {
var seln = this.replacedFragment;
if (!seln) return false;

var inner = seln.getEnd(L);
if ( // do we have a matching bracket pair?
!(inner instanceof Bracket) ||
inner !== seln.getEnd(R) ||
inner.sides[L].ch !== this.sides[L].ch ||
inner.sides[R].ch !== this.sides[R].ch
) {
return false;
}

var innerBlock = inner.getEnd(L);
// Nothing to unwrap if the bracket is empty; fall back to wrapping.
if (innerBlock.isEmpty()) return false;

var innerR = innerBlock.getEnd(R) as MQNode;

// Move the bracket's children to where the selection was
// and drop the bracket that was there
var children = innerBlock.children();
var brackFrag = seln.domFrag();
children.disown().adopt(cursor.parent, cursor[L], cursor[R]);
children.domFrag().insertBefore(brackFrag);
brackFrag.remove();

cursor.parent.bubble(function (node) {
node.reflow();
return undefined;
});
// remove the selection
cursor.insRightOf(innerR);

// Skip the announcement for the otherwise to-be created bracket and
// instead announce the unwrapped contents.
this.skipMathspeak = true;
cursor.controller.aria.queue(
children
.fold('', function (speech, child) {
return speech + ' ' + child.mathspeak();
})
.trim()
);
return true;
}
createLeftOf(cursor: Cursor) {
if (this.unwrapSelectedBracket(cursor)) return;
var brack;
if (!this.replacedFragment) {
// unless wrapping seln in brackets,
Expand Down
39 changes: 39 additions & 0 deletions test/unit/typing.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,45 @@ suite('typing with auto-replaces', function () {
assertLatex('1+2+3+4');
});

test('wrapping a bracketed selection in the same bracket unwraps it', function () {
mq.typedText('(ab)');
assertLatex('\\left(ab\\right)');
mq.keystroke('Shift-Home').typedText('(');
assertLatex('ab');
// cursor should go after contents
// so typing "(" inserts a new bracket
mq.typedText('(');
assertLatex('ab\\left(\\right)');
});

test('unwrapping only happens when the selection is exactly one bracket', function () {
mq.typedText('(a)(b)');
assertLatex('\\left(a\\right)\\left(b\\right)');
mq.keystroke('Shift-Home').typedText('(');
assertLatex('\\left(\\left(a\\right)\\left(b\\right)\\right)');
});

test('unwrapping only happens when the bracket type matches', function () {
mq.typedText('(ab)');
assertLatex('\\left(ab\\right)');
mq.keystroke('Shift-Home').typedText('[');
assertLatex('\\left[\\left(ab\\right)\\right]');
});

test('does not unwrap a mismatched bracket', function () {
mq.typedText('(ab]');
assertLatex('\\left(ab\\right]');
mq.keystroke('Shift-Home').typedText('(');
assertLatex('\\left(\\left(ab\\right]\\right)');
});

test('wrapping an empty bracket in the same bracket wraps rather than unwraps', function () {
mq.typedText('()');
assertLatex('\\left(\\right)');
mq.keystroke('Shift-Home').typedText('(');
assertLatex('\\left(\\left(\\right)\\right)');
});

test('backspacing close-bracket of 1+(2+3] (nothing after) then typing', function () {
mq.typedText('1+(2+3]');
assertLatex('1+\\left(2+3\\right]');
Expand Down