[MTMathListBuilder] enhanced \begin{cases} environment support
No Need to use the & character at line end. The previous notation is still supported.
This commit is contained in:
@@ -898,19 +898,21 @@ public class MTMathAtomFactory {
|
||||
|
||||
return table
|
||||
} else if env == "cases" {
|
||||
if table.numColumns != 2 {
|
||||
let message = "cases environment can only have 2 columns"
|
||||
if table.numColumns != 1 && table.numColumns != 2 {
|
||||
let message = "cases environment can have 1 or 2 columns"
|
||||
if error == nil {
|
||||
error = NSError(domain: MTParseError, code: MTParseErrors.invalidNumColumns.rawValue, userInfo: [NSLocalizedDescriptionKey:message])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
table.interRowAdditionalSpacing = 0
|
||||
table.interColumnSpacing = 18
|
||||
|
||||
|
||||
table.set(alignment: .left, forColumn: 0)
|
||||
table.set(alignment: .left, forColumn: 1)
|
||||
if table.numColumns == 2 {
|
||||
table.set(alignment: .left, forColumn: 1)
|
||||
}
|
||||
|
||||
let style = MTMathStyle(style: .text)
|
||||
for i in 0..<table.cells.count {
|
||||
|
||||
@@ -130,6 +130,111 @@ public struct MTMathListBuilder {
|
||||
_ = readCommand()
|
||||
}
|
||||
}
|
||||
|
||||
// Analyze cases environment content to detect if & characters are present
|
||||
mutating func analyzeCasesContent() -> Bool {
|
||||
let savedIndex = currentCharIndex
|
||||
var braceDepth = 0
|
||||
var hasAmpersand = false
|
||||
|
||||
while currentCharIndex < string.endIndex {
|
||||
let char = string[currentCharIndex]
|
||||
currentCharIndex = string.index(after: currentCharIndex)
|
||||
|
||||
switch char {
|
||||
case "{":
|
||||
braceDepth += 1
|
||||
case "}":
|
||||
braceDepth -= 1
|
||||
if braceDepth < 0 {
|
||||
// Found unmatched closing brace, likely end of environment content
|
||||
break
|
||||
}
|
||||
case "&":
|
||||
if braceDepth == 0 {
|
||||
// Found & at top level (not inside nested braces)
|
||||
hasAmpersand = true
|
||||
break
|
||||
}
|
||||
case "\\":
|
||||
// Skip the next character if it's a command
|
||||
if currentCharIndex < string.endIndex {
|
||||
let nextChar = string[currentCharIndex]
|
||||
if nextChar == "e" {
|
||||
// Check if this might be \end{cases}
|
||||
let remainingChars = string[currentCharIndex...]
|
||||
if remainingChars.hasPrefix("end{cases}") {
|
||||
break
|
||||
}
|
||||
}
|
||||
currentCharIndex = string.index(after: currentCharIndex)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Restore position
|
||||
currentCharIndex = savedIndex
|
||||
return hasAmpersand
|
||||
}
|
||||
|
||||
// Build single-column cases environment (no & separators)
|
||||
mutating func buildSingleColumnCases(firstList: MTMathList?, isRow: Bool) -> MTMathAtom? {
|
||||
let oldEnv = self.currentEnv
|
||||
currentEnv = MTEnvProperties(name: "cases")
|
||||
|
||||
var rows = [[MTMathList]]()
|
||||
var currentRow = 0
|
||||
|
||||
// Add first list if provided
|
||||
if let first = firstList {
|
||||
rows.append([first])
|
||||
if isRow {
|
||||
currentEnv!.numRows += 1
|
||||
currentRow += 1
|
||||
rows.append([MTMathList]())
|
||||
}
|
||||
} else {
|
||||
rows.append([MTMathList]())
|
||||
}
|
||||
|
||||
while !currentEnv!.ended && self.hasCharacters {
|
||||
let list = self.buildInternal(false)
|
||||
if list == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if currentEnv!.numRows > currentRow {
|
||||
// New row triggered by \\
|
||||
currentRow = currentEnv!.numRows
|
||||
rows.append([list!])
|
||||
} else {
|
||||
// Continue current row (should be single cell)
|
||||
if rows[currentRow].isEmpty {
|
||||
rows[currentRow].append(list!)
|
||||
} else {
|
||||
// Append to existing cell in single-column mode
|
||||
rows[currentRow][0].append(list!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !currentEnv!.ended {
|
||||
self.setError(.missingEnd, message: "Missing \\end")
|
||||
return nil
|
||||
}
|
||||
|
||||
var error: NSError? = self.error
|
||||
let table = MTMathAtomFactory.table(withEnvironment: "cases", rows: rows, error: &error)
|
||||
if table == nil && self.error == nil {
|
||||
self.error = error
|
||||
return nil
|
||||
}
|
||||
|
||||
self.currentEnv = oldEnv
|
||||
return table
|
||||
}
|
||||
|
||||
mutating func expectCharacter(_ ch: Character) -> Bool {
|
||||
MTAssertNotSpace(ch)
|
||||
@@ -970,9 +1075,19 @@ public struct MTMathListBuilder {
|
||||
}
|
||||
|
||||
mutating func buildTable(env: String?, firstList: MTMathList?, isRow: Bool) -> MTMathAtom? {
|
||||
// Special handling for cases environment - detect single vs two column structure
|
||||
if env == "cases" {
|
||||
let hasAmpersand = analyzeCasesContent()
|
||||
if !hasAmpersand {
|
||||
// Parse as single-column cases environment
|
||||
return buildSingleColumnCases(firstList: firstList, isRow: isRow)
|
||||
}
|
||||
// If ampersand found, continue with standard two-column parsing
|
||||
}
|
||||
|
||||
// Save the current env till an new one gets built.
|
||||
let oldEnv = self.currentEnv
|
||||
|
||||
|
||||
currentEnv = MTEnvProperties(name: env)
|
||||
|
||||
var currentRow = 0
|
||||
|
||||
Reference in New Issue
Block a user