package tex
import (
"fmt"
"strings"
"unicode/utf8"
"github.com/d2lang/mathjax-go/internal/mml"
)
type casesBeginItem struct {
end bool
entries int
closing *environmentEndItem
}
func (p *parser ) casesEnvironment (name string ) (nodes []*mml .Node , handled bool , err error ) {
if name != "numcases" && name != "subnumcases" {
return nil , false , nil
}
left , _ , err := p .readArgument ("begin" , false )
if err != nil {
return nil , true , err
}
if p .commandCasesBegin != nil {
if err := p .commandCasesBegin (); err != nil {
return nil , true , err
}
}
if err := p .checkEquationEnvironment (); err != nil {
return nil , true , err
}
frame := p .environmentOwner
frame .casesBegin = &casesBeginItem {end : true , entries : 2 }
array := &ordinaryArrayItem {begin : frame }
tags := p .amsTags ()
tags .start (name , true , true )
ended := false
defer func () {
if !ended && !array .popped {
tags .end ()
}
}()
table := node ("mtable" )
maximum := 0
appendRow := func (row *mml .Node ) error {
if len (row .Children ) > maximum {
maximum = len (row .Children )
}
tag , err := tags .getTag (p )
if err != nil {
return err
}
if tag != nil {
row = node ("mlabeledtr" , append ([]*mml .Node {tag }, row .Children ...)...)
}
table .AppendChild (row )
tags .clearTag ()
return nil
}
spacing := newEquationTableState (appendRow )
spacing .initialSpacing = ".2em"
equation := &equationRowState {table : spacing }
err = p .parseArrayBody ("" , arrayBodyOwner {
environment : frame ,
ordinary : array ,
rules : spacing .rules ,
configure : func (sub *parser ) {
sub .arrayCell .equation , sub .arrayCell .numCases = equation , true
},
prepareEntry : func (sub *parser , item *cellItem ) ([]*mml .Node , error ) {
if item == nil || !item .numCasesText {
return nil , nil
}
return sub .prepareNumCasesText (name )
},
hasEntries : func () bool { return len (equation .entries ) != 0 },
endEntry : func (children []*mml .Node , _ *arrayCellState ) error {
equation .endEntry (children )
return nil
},
endRow : equation .endRow ,
addSpacing : spacing .addSpacing ,
})
if err != nil {
return nil , true , err
}
if array .popped {
children , err := p .continueCasesBegin (frame , []*mml .Node {row (array .nodes , true )})
if err != nil {
return nil , true , err
}
if frame .closed && frame .casesBegin .closing == nil {
return children , true , nil
}
if frame .closed {
p .commandCasesLeft = &environmentEndItem {name : frame .casesBegin .closing .name }
return nil , true , nil
}
if len (children ) == 0 {
return nil , true , fmt .Errorf ("Cases left block requires a pending node" )
}
first := children [0 ]
original := p .copyNode (first )
if err := p .empheqAddLeft (first , original , left +"\\empheqlbrace\\," , "numcases-left" ); err != nil {
return nil , true , err
}
if err := p .closeEnvironment (frame , &environmentEndItem {name : frame .casesBegin .closing .name }); err != nil {
return nil , true , err
}
return children , true , nil
}
resetTableAttributes (table ,
"displaystyle" , false ,
"rowspacing" , ".2em" ,
"columnalign" , repeatAMSEqnArrayDefinition ("left left" , maximum ),
"columnspacing" , "1em" ,
"side" , "right" ,
"minlabelspacing" , "0.8em" ,
)
spacing .applySpacing (table )
tags .end ()
ended = true
if array .end == nil {
return nil , true , frame .missing ()
}
if err := p .closeEnvironment (frame , array .end ); err != nil {
return nil , true , err
}
table = finishArrayRules (table )
original := p .copyNode (table )
if err := p .empheqAddLeft (table , original , left +"\\empheqlbrace\\," , "numcases-left" ); err != nil {
return nil , true , err
}
if err := p .closeEnvironment (frame , &environmentEndItem {name : frame .casesBegin .closing .name }); err != nil {
return nil , true , err
}
return []*mml .Node {table }, true , nil
}
func (p *parser ) continueCasesBegin (begin *environmentFrame , prefix []*mml .Node ) ([]*mml .Node , error ) {
previous , array := p .environmentRow , p .ordinaryArray
cell , matrixClose , entry , braket := p .arrayCell , p .matrixClose , p .cdArrayEntry , p .braketOwner
p .environmentRow , p .ordinaryArray = begin , nil
p .arrayCell , p .matrixClose , p .cdArrayEntry , p .braketOwner = nil , false , false , nil
defer func () {
p .environmentRow , p .ordinaryArray = previous , array
p .arrayCell , p .matrixClose , p .cdArrayEntry , p .braketOwner = cell , matrixClose , entry , braket
}()
children , _ , err := p .parseRowContinuation (0 , false , false , nil , "" , prefix )
if err != nil {
return nil , err
}
if !begin .closed && (begin .casesBegin .end || begin .casesBegin .closing == nil ) {
return nil , begin .missing ()
}
return children , nil
}
func numCasesTextEnd(source string , start int ) (int , error ) {
braces := 0
for i := start ; i < len (source ); {
switch source [i ] {
case '{' :
braces ++
i ++
case '}' :
if braces == 0 {
return i , nil
}
braces --
i ++
case '&' :
if braces == 0 {
return i , texError ("ExtraCasesAlignTab" , "Extra alignment tab in text for numcase environment" )
}
i ++
case '\\' :
if braces != 0 {
i ++
continue
}
if i +1 == len (source ) {
return i , fmt .Errorf ("NumCases text scanner has no control-sequence token" )
}
j := i + 1
for j < len (source ) && isASCIILetter (rune (source [j ])) {
j ++
}
if j == i +1 {
r , size := utf8 .DecodeRuneInString (source [j :])
if r == '\n' || r == '\r' || r == '\u2028' || r == '\u2029' {
return i , fmt .Errorf ("NumCases text scanner has no control-sequence token" )
}
j += size
}
command := source [i +1 : j ]
if command == "\\" || command == "cr" || command == "end" || command == "label" {
return i , nil
}
if isASCIILetter (rune (source [i +1 ])) {
i = j - 1
} else {
i ++
}
default :
_ , size := utf8 .DecodeRuneInString (source [i :])
i += size
}
}
return len (source ), nil
}
func (p *parser ) prepareNumCasesText (environment string ) ([]*mml .Node , error ) {
end , err := numCasesTextEnd (p .source , p .pos )
if err != nil {
return nil , err
}
if end > len (p .source ) {
end = len (p .source )
}
text := strings .TrimLeftFunc (p .source [p .pos :end ], internalTextSpace )
p .pos = end
return p .internalMath (text , "" , true )
}
func (p *parser ) finishCasesLeft (first *mml .Node , left string ) error {
if first == nil {
return fmt .Errorf ("Cases left block requires a pending node" )
}
original := p .copyNode (first )
return p .empheqAddLeft (first , original , left +"\\empheqlbrace\\," , "numcases-left" )
}
The pages are generated with Golds v0.8.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .