[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
|
return table
|
||||||
} else if env == "cases" {
|
} else if env == "cases" {
|
||||||
if table.numColumns != 2 {
|
if table.numColumns != 1 && table.numColumns != 2 {
|
||||||
let message = "cases environment can only have 2 columns"
|
let message = "cases environment can have 1 or 2 columns"
|
||||||
if error == nil {
|
if error == nil {
|
||||||
error = NSError(domain: MTParseError, code: MTParseErrors.invalidNumColumns.rawValue, userInfo: [NSLocalizedDescriptionKey:message])
|
error = NSError(domain: MTParseError, code: MTParseErrors.invalidNumColumns.rawValue, userInfo: [NSLocalizedDescriptionKey:message])
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
table.interRowAdditionalSpacing = 0
|
table.interRowAdditionalSpacing = 0
|
||||||
table.interColumnSpacing = 18
|
table.interColumnSpacing = 18
|
||||||
|
|
||||||
table.set(alignment: .left, forColumn: 0)
|
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)
|
let style = MTMathStyle(style: .text)
|
||||||
for i in 0..<table.cells.count {
|
for i in 0..<table.cells.count {
|
||||||
|
|||||||
@@ -130,6 +130,111 @@ public struct MTMathListBuilder {
|
|||||||
_ = readCommand()
|
_ = 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 {
|
mutating func expectCharacter(_ ch: Character) -> Bool {
|
||||||
MTAssertNotSpace(ch)
|
MTAssertNotSpace(ch)
|
||||||
@@ -970,9 +1075,19 @@ public struct MTMathListBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mutating func buildTable(env: String?, firstList: MTMathList?, isRow: Bool) -> MTMathAtom? {
|
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.
|
// Save the current env till an new one gets built.
|
||||||
let oldEnv = self.currentEnv
|
let oldEnv = self.currentEnv
|
||||||
|
|
||||||
currentEnv = MTEnvProperties(name: env)
|
currentEnv = MTEnvProperties(name: env)
|
||||||
|
|
||||||
var currentRow = 0
|
var currentRow = 0
|
||||||
|
|||||||
Reference in New Issue
Block a user