mirror of
https://github.com/RWejlgaard/org.git
synced 2026-05-06 04:34:45 +00:00
move latex junk to another file
This commit is contained in:
parent
c8114a5f72
commit
69b2ead2b6
2 changed files with 380 additions and 377 deletions
380
internal/ui/latex.go
Normal file
380
internal/ui/latex.go
Normal file
|
|
@ -0,0 +1,380 @@
|
||||||
|
package ui
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// renderLatexMath converts LaTeX math expressions to Unicode for terminal display
|
||||||
|
func renderLatexMath(latex string) string {
|
||||||
|
result := latex
|
||||||
|
|
||||||
|
// Remove LaTeX math delimiters
|
||||||
|
result = strings.ReplaceAll(result, `\(`, "")
|
||||||
|
result = strings.ReplaceAll(result, `\)`, "")
|
||||||
|
result = strings.ReplaceAll(result, `\[`, "")
|
||||||
|
result = strings.ReplaceAll(result, `\]`, "")
|
||||||
|
result = strings.ReplaceAll(result, `$$`, "")
|
||||||
|
|
||||||
|
// Remove single $ delimiters (being careful not to remove actual dollar signs in text)
|
||||||
|
// Simple approach: if line starts/ends with $, remove it
|
||||||
|
result = strings.TrimSpace(result)
|
||||||
|
if strings.HasPrefix(result, "$") && strings.HasSuffix(result, "$") && len(result) > 2 {
|
||||||
|
result = result[1 : len(result)-1]
|
||||||
|
result = strings.TrimSpace(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map of common LaTeX commands to Unicode equivalents
|
||||||
|
replacements := map[string]string{
|
||||||
|
// Greek letters (lowercase)
|
||||||
|
`\alpha`: "α",
|
||||||
|
`\beta`: "β",
|
||||||
|
`\gamma`: "γ",
|
||||||
|
`\delta`: "δ",
|
||||||
|
`\epsilon`: "ε",
|
||||||
|
`\zeta`: "ζ",
|
||||||
|
`\eta`: "η",
|
||||||
|
`\theta`: "θ",
|
||||||
|
`\iota`: "ι",
|
||||||
|
`\kappa`: "κ",
|
||||||
|
`\lambda`: "λ",
|
||||||
|
`\mu`: "μ",
|
||||||
|
`\nu`: "ν",
|
||||||
|
`\xi`: "ξ",
|
||||||
|
`\pi`: "π",
|
||||||
|
`\rho`: "ρ",
|
||||||
|
`\sigma`: "σ",
|
||||||
|
`\tau`: "τ",
|
||||||
|
`\upsilon`: "υ",
|
||||||
|
`\phi`: "φ",
|
||||||
|
`\chi`: "χ",
|
||||||
|
`\psi`: "ψ",
|
||||||
|
`\omega`: "ω",
|
||||||
|
|
||||||
|
// Greek letters (uppercase)
|
||||||
|
`\Gamma`: "Γ",
|
||||||
|
`\Delta`: "Δ",
|
||||||
|
`\Theta`: "Θ",
|
||||||
|
`\Lambda`: "Λ",
|
||||||
|
`\Xi`: "Ξ",
|
||||||
|
`\Pi`: "Π",
|
||||||
|
`\Sigma`: "Σ",
|
||||||
|
`\Upsilon`: "Υ",
|
||||||
|
`\Phi`: "Φ",
|
||||||
|
`\Psi`: "Ψ",
|
||||||
|
`\Omega`: "Ω",
|
||||||
|
|
||||||
|
// Math operators
|
||||||
|
`\times`: "×",
|
||||||
|
`\div`: "÷",
|
||||||
|
`\pm`: "±",
|
||||||
|
`\mp`: "∓",
|
||||||
|
`\cdot`: "·",
|
||||||
|
`\star`: "⋆",
|
||||||
|
`\ast`: "∗",
|
||||||
|
`\circ`: "∘",
|
||||||
|
`\bullet`: "•",
|
||||||
|
|
||||||
|
// Relations
|
||||||
|
`\le`: "≤",
|
||||||
|
`\ge`: "≥",
|
||||||
|
`\leq`: "≤",
|
||||||
|
`\geq`: "≥",
|
||||||
|
`\ne`: "≠",
|
||||||
|
`\neq`: "≠",
|
||||||
|
`\approx`: "≈",
|
||||||
|
`\equiv`: "≡",
|
||||||
|
`\sim`: "∼",
|
||||||
|
`\simeq`: "≃",
|
||||||
|
`\propto`: "∝",
|
||||||
|
|
||||||
|
// Arrows
|
||||||
|
`\to`: "→",
|
||||||
|
`\rightarrow`: "→",
|
||||||
|
`\leftarrow`: "←",
|
||||||
|
`\Rightarrow`: "⇒",
|
||||||
|
`\Leftarrow`: "⇐",
|
||||||
|
`\mapsto`: "↦",
|
||||||
|
|
||||||
|
// Set theory
|
||||||
|
`\in`: "∈",
|
||||||
|
`\notin`: "∉",
|
||||||
|
`\subset`: "⊂",
|
||||||
|
`\supset`: "⊃",
|
||||||
|
`\subseteq`: "⊆",
|
||||||
|
`\supseteq`: "⊇",
|
||||||
|
`\cup`: "∪",
|
||||||
|
`\cap`: "∩",
|
||||||
|
`\emptyset`: "∅",
|
||||||
|
`\forall`: "∀",
|
||||||
|
`\exists`: "∃",
|
||||||
|
|
||||||
|
// Calculus
|
||||||
|
`\partial`: "∂",
|
||||||
|
`\nabla`: "∇",
|
||||||
|
`\int`: "∫",
|
||||||
|
`\sum`: "∑",
|
||||||
|
`\prod`: "∏",
|
||||||
|
`\infty`: "∞",
|
||||||
|
|
||||||
|
// Logic
|
||||||
|
`\land`: "∧",
|
||||||
|
`\lor`: "∨",
|
||||||
|
`\lnot`: "¬",
|
||||||
|
`\neg`: "¬",
|
||||||
|
`\wedge`: "∧",
|
||||||
|
`\vee`: "∨",
|
||||||
|
|
||||||
|
// Special symbols
|
||||||
|
`\hbar`: "ℏ",
|
||||||
|
`\ell`: "ℓ",
|
||||||
|
`\Re`: "ℜ",
|
||||||
|
`\Im`: "ℑ",
|
||||||
|
`\angle`: "∠",
|
||||||
|
`\triangle`: "△",
|
||||||
|
`\square`: "□",
|
||||||
|
`\degree`: "°",
|
||||||
|
|
||||||
|
// Superscripts (common ones)
|
||||||
|
`^0`: "⁰",
|
||||||
|
`^1`: "¹",
|
||||||
|
`^2`: "²",
|
||||||
|
`^3`: "³",
|
||||||
|
`^4`: "⁴",
|
||||||
|
`^5`: "⁵",
|
||||||
|
`^6`: "⁶",
|
||||||
|
`^7`: "⁷",
|
||||||
|
`^8`: "⁸",
|
||||||
|
`^9`: "⁹",
|
||||||
|
`^+`: "⁺",
|
||||||
|
`^-`: "⁻",
|
||||||
|
`^=`: "⁼",
|
||||||
|
`^(`: "⁽",
|
||||||
|
`^)`: "⁾",
|
||||||
|
|
||||||
|
// Subscripts (common ones)
|
||||||
|
`_0`: "₀",
|
||||||
|
`_1`: "₁",
|
||||||
|
`_2`: "₂",
|
||||||
|
`_3`: "₃",
|
||||||
|
`_4`: "₄",
|
||||||
|
`_5`: "₅",
|
||||||
|
`_6`: "₆",
|
||||||
|
`_7`: "₇",
|
||||||
|
`_8`: "₈",
|
||||||
|
`_9`: "₉",
|
||||||
|
`_+`: "₊",
|
||||||
|
`_-`: "₋",
|
||||||
|
`_=`: "₌",
|
||||||
|
`_(`: "₍",
|
||||||
|
`_)`: "₎",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply all replacements
|
||||||
|
for latexCmd, unicode := range replacements {
|
||||||
|
result = strings.ReplaceAll(result, latexCmd, unicode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle simple fractions \frac{a}{b} -> a/b
|
||||||
|
result = handleFractions(result)
|
||||||
|
|
||||||
|
// Handle square roots \sqrt{x} -> √(x)
|
||||||
|
result = handleSquareRoots(result)
|
||||||
|
|
||||||
|
// Handle superscripts ^{...} and subscripts _{...}
|
||||||
|
result = handleSuperscripts(result)
|
||||||
|
result = handleSubscripts(result)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleFractions converts \frac{numerator}{denominator} to numerator/denominator
|
||||||
|
func handleFractions(text string) string {
|
||||||
|
// Simple regex-free approach for basic fractions
|
||||||
|
result := text
|
||||||
|
for {
|
||||||
|
start := strings.Index(result, `\frac{`)
|
||||||
|
if start == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the numerator
|
||||||
|
numStart := start + 6
|
||||||
|
numEnd, numerator := findBracedContent(result, numStart)
|
||||||
|
if numEnd == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the denominator
|
||||||
|
if numEnd >= len(result) || result[numEnd] != '{' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
denomEnd, denominator := findBracedContent(result, numEnd+1)
|
||||||
|
if denomEnd == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace \frac{num}{denom} with (num)/(denom)
|
||||||
|
replacement := "(" + numerator + ")/(" + denominator + ")"
|
||||||
|
result = result[:start] + replacement + result[denomEnd:]
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleSquareRoots converts \sqrt{x} to √(x)
|
||||||
|
func handleSquareRoots(text string) string {
|
||||||
|
result := text
|
||||||
|
for {
|
||||||
|
start := strings.Index(result, `\sqrt{`)
|
||||||
|
if start == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the content
|
||||||
|
contentStart := start + 6
|
||||||
|
contentEnd, content := findBracedContent(result, contentStart)
|
||||||
|
if contentEnd == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace \sqrt{content} with √(content)
|
||||||
|
replacement := "√(" + content + ")"
|
||||||
|
result = result[:start] + replacement + result[contentEnd:]
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// findBracedContent finds content within braces starting at position i
|
||||||
|
// Returns the position after the closing brace and the content
|
||||||
|
func findBracedContent(text string, start int) (int, string) {
|
||||||
|
if start >= len(text) {
|
||||||
|
return -1, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
depth := 1
|
||||||
|
i := start
|
||||||
|
|
||||||
|
for i < len(text) && depth > 0 {
|
||||||
|
if text[i] == '{' {
|
||||||
|
depth++
|
||||||
|
} else if text[i] == '}' {
|
||||||
|
depth--
|
||||||
|
if depth == 0 {
|
||||||
|
return i + 1, text[start:i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleSuperscripts converts ^{...} to Unicode superscripts where possible
|
||||||
|
func handleSuperscripts(text string) string {
|
||||||
|
superscriptMap := map[rune]string{
|
||||||
|
'0': "⁰", '1': "¹", '2': "²", '3': "³", '4': "⁴",
|
||||||
|
'5': "⁵", '6': "⁶", '7': "⁷", '8': "⁸", '9': "⁹",
|
||||||
|
'a': "ᵃ", 'b': "ᵇ", 'c': "ᶜ", 'd': "ᵈ", 'e': "ᵉ",
|
||||||
|
'f': "ᶠ", 'g': "ᵍ", 'h': "ʰ", 'i': "ⁱ", 'j': "ʲ",
|
||||||
|
'k': "ᵏ", 'l': "ˡ", 'm': "ᵐ", 'n': "ⁿ", 'o': "ᵒ",
|
||||||
|
'p': "ᵖ", 'r': "ʳ", 's': "ˢ", 't': "ᵗ", 'u': "ᵘ",
|
||||||
|
'v': "ᵛ", 'w': "ʷ", 'x': "ˣ", 'y': "ʸ", 'z': "ᶻ",
|
||||||
|
'A': "ᴬ", 'B': "ᴮ", 'D': "ᴰ", 'E': "ᴱ", 'G': "ᴳ",
|
||||||
|
'H': "ᴴ", 'I': "ᴵ", 'J': "ᴶ", 'K': "ᴷ", 'L': "ᴸ",
|
||||||
|
'M': "ᴹ", 'N': "ᴺ", 'O': "ᴼ", 'P': "ᴾ", 'R': "ᴿ",
|
||||||
|
'T': "ᵀ", 'U': "ᵁ", 'V': "ⱽ", 'W': "ᵂ",
|
||||||
|
'+': "⁺", '-': "⁻", '=': "⁼", '(': "⁽", ')': "⁾",
|
||||||
|
}
|
||||||
|
|
||||||
|
result := text
|
||||||
|
|
||||||
|
// Handle ^{...} format
|
||||||
|
for {
|
||||||
|
start := strings.Index(result, "^{")
|
||||||
|
if start == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
contentStart := start + 2
|
||||||
|
contentEnd, content := findBracedContent(result, contentStart)
|
||||||
|
if contentEnd == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert content to superscript
|
||||||
|
var superscript strings.Builder
|
||||||
|
for _, ch := range content {
|
||||||
|
if sup, ok := superscriptMap[ch]; ok {
|
||||||
|
superscript.WriteString(sup)
|
||||||
|
} else {
|
||||||
|
// If no superscript version exists, wrap in parentheses
|
||||||
|
superscript.WriteRune(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = result[:start] + superscript.String() + result[contentEnd:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle simple ^x format (single character without braces)
|
||||||
|
for i := 0; i < len(result)-1; i++ {
|
||||||
|
if result[i] == '^' && result[i+1] != '{' {
|
||||||
|
ch := rune(result[i+1])
|
||||||
|
if sup, ok := superscriptMap[ch]; ok {
|
||||||
|
result = result[:i] + sup + result[i+2:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleSubscripts converts _{...} to Unicode subscripts where possible
|
||||||
|
func handleSubscripts(text string) string {
|
||||||
|
subscriptMap := map[rune]string{
|
||||||
|
'0': "₀", '1': "₁", '2': "₂", '3': "₃", '4': "₄",
|
||||||
|
'5': "₅", '6': "₆", '7': "₇", '8': "₈", '9': "₉",
|
||||||
|
'a': "ₐ", 'e': "ₑ", 'h': "ₕ", 'i': "ᵢ", 'j': "ⱼ",
|
||||||
|
'k': "ₖ", 'l': "ₗ", 'm': "ₘ", 'n': "ₙ", 'o': "ₒ",
|
||||||
|
'p': "ₚ", 'r': "ᵣ", 's': "ₛ", 't': "ₜ", 'u': "ᵤ",
|
||||||
|
'v': "ᵥ", 'x': "ₓ",
|
||||||
|
'+': "₊", '-': "₋", '=': "₌", '(': "₍", ')': "₎",
|
||||||
|
}
|
||||||
|
|
||||||
|
result := text
|
||||||
|
|
||||||
|
// Handle _{...} format
|
||||||
|
for {
|
||||||
|
start := strings.Index(result, "_{")
|
||||||
|
if start == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
contentStart := start + 2
|
||||||
|
contentEnd, content := findBracedContent(result, contentStart)
|
||||||
|
if contentEnd == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert content to subscript
|
||||||
|
var subscript strings.Builder
|
||||||
|
for _, ch := range content {
|
||||||
|
if sub, ok := subscriptMap[ch]; ok {
|
||||||
|
subscript.WriteString(sub)
|
||||||
|
} else {
|
||||||
|
// If no subscript version exists, wrap in parentheses
|
||||||
|
subscript.WriteRune(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = result[:start] + subscript.String() + result[contentEnd:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle simple _x format (single character without braces)
|
||||||
|
for i := 0; i < len(result)-1; i++ {
|
||||||
|
if result[i] == '_' && result[i+1] != '{' {
|
||||||
|
ch := rune(result[i+1])
|
||||||
|
if sub, ok := subscriptMap[ch]; ok {
|
||||||
|
result = result[:i] + sub + result[i+2:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
@ -751,383 +751,6 @@ func highlightCode(code, language string) string {
|
||||||
return strings.TrimRight(buf.String(), "\n")
|
return strings.TrimRight(buf.String(), "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
// renderLatexMath converts LaTeX math expressions to Unicode for terminal display
|
|
||||||
func renderLatexMath(latex string) string {
|
|
||||||
result := latex
|
|
||||||
|
|
||||||
// Remove LaTeX math delimiters
|
|
||||||
result = strings.ReplaceAll(result, `\(`, "")
|
|
||||||
result = strings.ReplaceAll(result, `\)`, "")
|
|
||||||
result = strings.ReplaceAll(result, `\[`, "")
|
|
||||||
result = strings.ReplaceAll(result, `\]`, "")
|
|
||||||
result = strings.ReplaceAll(result, `$$`, "")
|
|
||||||
|
|
||||||
// Remove single $ delimiters (being careful not to remove actual dollar signs in text)
|
|
||||||
// Simple approach: if line starts/ends with $, remove it
|
|
||||||
result = strings.TrimSpace(result)
|
|
||||||
if strings.HasPrefix(result, "$") && strings.HasSuffix(result, "$") && len(result) > 2 {
|
|
||||||
result = result[1 : len(result)-1]
|
|
||||||
result = strings.TrimSpace(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map of common LaTeX commands to Unicode equivalents
|
|
||||||
replacements := map[string]string{
|
|
||||||
// Greek letters (lowercase)
|
|
||||||
`\alpha`: "α",
|
|
||||||
`\beta`: "β",
|
|
||||||
`\gamma`: "γ",
|
|
||||||
`\delta`: "δ",
|
|
||||||
`\epsilon`: "ε",
|
|
||||||
`\zeta`: "ζ",
|
|
||||||
`\eta`: "η",
|
|
||||||
`\theta`: "θ",
|
|
||||||
`\iota`: "ι",
|
|
||||||
`\kappa`: "κ",
|
|
||||||
`\lambda`: "λ",
|
|
||||||
`\mu`: "μ",
|
|
||||||
`\nu`: "ν",
|
|
||||||
`\xi`: "ξ",
|
|
||||||
`\pi`: "π",
|
|
||||||
`\rho`: "ρ",
|
|
||||||
`\sigma`: "σ",
|
|
||||||
`\tau`: "τ",
|
|
||||||
`\upsilon`: "υ",
|
|
||||||
`\phi`: "φ",
|
|
||||||
`\chi`: "χ",
|
|
||||||
`\psi`: "ψ",
|
|
||||||
`\omega`: "ω",
|
|
||||||
|
|
||||||
// Greek letters (uppercase)
|
|
||||||
`\Gamma`: "Γ",
|
|
||||||
`\Delta`: "Δ",
|
|
||||||
`\Theta`: "Θ",
|
|
||||||
`\Lambda`: "Λ",
|
|
||||||
`\Xi`: "Ξ",
|
|
||||||
`\Pi`: "Π",
|
|
||||||
`\Sigma`: "Σ",
|
|
||||||
`\Upsilon`: "Υ",
|
|
||||||
`\Phi`: "Φ",
|
|
||||||
`\Psi`: "Ψ",
|
|
||||||
`\Omega`: "Ω",
|
|
||||||
|
|
||||||
// Math operators
|
|
||||||
`\times`: "×",
|
|
||||||
`\div`: "÷",
|
|
||||||
`\pm`: "±",
|
|
||||||
`\mp`: "∓",
|
|
||||||
`\cdot`: "·",
|
|
||||||
`\star`: "⋆",
|
|
||||||
`\ast`: "∗",
|
|
||||||
`\circ`: "∘",
|
|
||||||
`\bullet`: "•",
|
|
||||||
|
|
||||||
// Relations
|
|
||||||
`\le`: "≤",
|
|
||||||
`\ge`: "≥",
|
|
||||||
`\leq`: "≤",
|
|
||||||
`\geq`: "≥",
|
|
||||||
`\ne`: "≠",
|
|
||||||
`\neq`: "≠",
|
|
||||||
`\approx`: "≈",
|
|
||||||
`\equiv`: "≡",
|
|
||||||
`\sim`: "∼",
|
|
||||||
`\simeq`: "≃",
|
|
||||||
`\propto`: "∝",
|
|
||||||
|
|
||||||
// Arrows
|
|
||||||
`\to`: "→",
|
|
||||||
`\rightarrow`: "→",
|
|
||||||
`\leftarrow`: "←",
|
|
||||||
`\Rightarrow`: "⇒",
|
|
||||||
`\Leftarrow`: "⇐",
|
|
||||||
`\mapsto`: "↦",
|
|
||||||
|
|
||||||
// Set theory
|
|
||||||
`\in`: "∈",
|
|
||||||
`\notin`: "∉",
|
|
||||||
`\subset`: "⊂",
|
|
||||||
`\supset`: "⊃",
|
|
||||||
`\subseteq`: "⊆",
|
|
||||||
`\supseteq`: "⊇",
|
|
||||||
`\cup`: "∪",
|
|
||||||
`\cap`: "∩",
|
|
||||||
`\emptyset`: "∅",
|
|
||||||
`\forall`: "∀",
|
|
||||||
`\exists`: "∃",
|
|
||||||
|
|
||||||
// Calculus
|
|
||||||
`\partial`: "∂",
|
|
||||||
`\nabla`: "∇",
|
|
||||||
`\int`: "∫",
|
|
||||||
`\sum`: "∑",
|
|
||||||
`\prod`: "∏",
|
|
||||||
`\infty`: "∞",
|
|
||||||
|
|
||||||
// Logic
|
|
||||||
`\land`: "∧",
|
|
||||||
`\lor`: "∨",
|
|
||||||
`\lnot`: "¬",
|
|
||||||
`\neg`: "¬",
|
|
||||||
`\wedge`: "∧",
|
|
||||||
`\vee`: "∨",
|
|
||||||
|
|
||||||
// Special symbols
|
|
||||||
`\hbar`: "ℏ",
|
|
||||||
`\ell`: "ℓ",
|
|
||||||
`\Re`: "ℜ",
|
|
||||||
`\Im`: "ℑ",
|
|
||||||
`\angle`: "∠",
|
|
||||||
`\triangle`: "△",
|
|
||||||
`\square`: "□",
|
|
||||||
`\degree`: "°",
|
|
||||||
|
|
||||||
// Superscripts (common ones)
|
|
||||||
`^0`: "⁰",
|
|
||||||
`^1`: "¹",
|
|
||||||
`^2`: "²",
|
|
||||||
`^3`: "³",
|
|
||||||
`^4`: "⁴",
|
|
||||||
`^5`: "⁵",
|
|
||||||
`^6`: "⁶",
|
|
||||||
`^7`: "⁷",
|
|
||||||
`^8`: "⁸",
|
|
||||||
`^9`: "⁹",
|
|
||||||
`^+`: "⁺",
|
|
||||||
`^-`: "⁻",
|
|
||||||
`^=`: "⁼",
|
|
||||||
`^(`: "⁽",
|
|
||||||
`^)`: "⁾",
|
|
||||||
|
|
||||||
// Subscripts (common ones)
|
|
||||||
`_0`: "₀",
|
|
||||||
`_1`: "₁",
|
|
||||||
`_2`: "₂",
|
|
||||||
`_3`: "₃",
|
|
||||||
`_4`: "₄",
|
|
||||||
`_5`: "₅",
|
|
||||||
`_6`: "₆",
|
|
||||||
`_7`: "₇",
|
|
||||||
`_8`: "₈",
|
|
||||||
`_9`: "₉",
|
|
||||||
`_+`: "₊",
|
|
||||||
`_-`: "₋",
|
|
||||||
`_=`: "₌",
|
|
||||||
`_(`: "₍",
|
|
||||||
`_)`: "₎",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply all replacements
|
|
||||||
for latexCmd, unicode := range replacements {
|
|
||||||
result = strings.ReplaceAll(result, latexCmd, unicode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle simple fractions \frac{a}{b} -> a/b
|
|
||||||
result = handleFractions(result)
|
|
||||||
|
|
||||||
// Handle square roots \sqrt{x} -> √(x)
|
|
||||||
result = handleSquareRoots(result)
|
|
||||||
|
|
||||||
// Handle superscripts ^{...} and subscripts _{...}
|
|
||||||
result = handleSuperscripts(result)
|
|
||||||
result = handleSubscripts(result)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// handleFractions converts \frac{numerator}{denominator} to numerator/denominator
|
|
||||||
func handleFractions(text string) string {
|
|
||||||
// Simple regex-free approach for basic fractions
|
|
||||||
result := text
|
|
||||||
for {
|
|
||||||
start := strings.Index(result, `\frac{`)
|
|
||||||
if start == -1 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the numerator
|
|
||||||
numStart := start + 6
|
|
||||||
numEnd, numerator := findBracedContent(result, numStart)
|
|
||||||
if numEnd == -1 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the denominator
|
|
||||||
if numEnd >= len(result) || result[numEnd] != '{' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
denomEnd, denominator := findBracedContent(result, numEnd+1)
|
|
||||||
if denomEnd == -1 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace \frac{num}{denom} with (num)/(denom)
|
|
||||||
replacement := "(" + numerator + ")/(" + denominator + ")"
|
|
||||||
result = result[:start] + replacement + result[denomEnd:]
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// handleSquareRoots converts \sqrt{x} to √(x)
|
|
||||||
func handleSquareRoots(text string) string {
|
|
||||||
result := text
|
|
||||||
for {
|
|
||||||
start := strings.Index(result, `\sqrt{`)
|
|
||||||
if start == -1 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the content
|
|
||||||
contentStart := start + 6
|
|
||||||
contentEnd, content := findBracedContent(result, contentStart)
|
|
||||||
if contentEnd == -1 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace \sqrt{content} with √(content)
|
|
||||||
replacement := "√(" + content + ")"
|
|
||||||
result = result[:start] + replacement + result[contentEnd:]
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// findBracedContent finds content within braces starting at position i
|
|
||||||
// Returns the position after the closing brace and the content
|
|
||||||
func findBracedContent(text string, start int) (int, string) {
|
|
||||||
if start >= len(text) {
|
|
||||||
return -1, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
depth := 1
|
|
||||||
i := start
|
|
||||||
|
|
||||||
for i < len(text) && depth > 0 {
|
|
||||||
if text[i] == '{' {
|
|
||||||
depth++
|
|
||||||
} else if text[i] == '}' {
|
|
||||||
depth--
|
|
||||||
if depth == 0 {
|
|
||||||
return i + 1, text[start:i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// handleSuperscripts converts ^{...} to Unicode superscripts where possible
|
|
||||||
func handleSuperscripts(text string) string {
|
|
||||||
superscriptMap := map[rune]string{
|
|
||||||
'0': "⁰", '1': "¹", '2': "²", '3': "³", '4': "⁴",
|
|
||||||
'5': "⁵", '6': "⁶", '7': "⁷", '8': "⁸", '9': "⁹",
|
|
||||||
'a': "ᵃ", 'b': "ᵇ", 'c': "ᶜ", 'd': "ᵈ", 'e': "ᵉ",
|
|
||||||
'f': "ᶠ", 'g': "ᵍ", 'h': "ʰ", 'i': "ⁱ", 'j': "ʲ",
|
|
||||||
'k': "ᵏ", 'l': "ˡ", 'm': "ᵐ", 'n': "ⁿ", 'o': "ᵒ",
|
|
||||||
'p': "ᵖ", 'r': "ʳ", 's': "ˢ", 't': "ᵗ", 'u': "ᵘ",
|
|
||||||
'v': "ᵛ", 'w': "ʷ", 'x': "ˣ", 'y': "ʸ", 'z': "ᶻ",
|
|
||||||
'A': "ᴬ", 'B': "ᴮ", 'D': "ᴰ", 'E': "ᴱ", 'G': "ᴳ",
|
|
||||||
'H': "ᴴ", 'I': "ᴵ", 'J': "ᴶ", 'K': "ᴷ", 'L': "ᴸ",
|
|
||||||
'M': "ᴹ", 'N': "ᴺ", 'O': "ᴼ", 'P': "ᴾ", 'R': "ᴿ",
|
|
||||||
'T': "ᵀ", 'U': "ᵁ", 'V': "ⱽ", 'W': "ᵂ",
|
|
||||||
'+': "⁺", '-': "⁻", '=': "⁼", '(': "⁽", ')': "⁾",
|
|
||||||
}
|
|
||||||
|
|
||||||
result := text
|
|
||||||
|
|
||||||
// Handle ^{...} format
|
|
||||||
for {
|
|
||||||
start := strings.Index(result, "^{")
|
|
||||||
if start == -1 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
contentStart := start + 2
|
|
||||||
contentEnd, content := findBracedContent(result, contentStart)
|
|
||||||
if contentEnd == -1 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert content to superscript
|
|
||||||
var superscript strings.Builder
|
|
||||||
for _, ch := range content {
|
|
||||||
if sup, ok := superscriptMap[ch]; ok {
|
|
||||||
superscript.WriteString(sup)
|
|
||||||
} else {
|
|
||||||
// If no superscript version exists, wrap in parentheses
|
|
||||||
superscript.WriteRune(ch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result = result[:start] + superscript.String() + result[contentEnd:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle simple ^x format (single character without braces)
|
|
||||||
for i := 0; i < len(result)-1; i++ {
|
|
||||||
if result[i] == '^' && result[i+1] != '{' {
|
|
||||||
ch := rune(result[i+1])
|
|
||||||
if sup, ok := superscriptMap[ch]; ok {
|
|
||||||
result = result[:i] + sup + result[i+2:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// handleSubscripts converts _{...} to Unicode subscripts where possible
|
|
||||||
func handleSubscripts(text string) string {
|
|
||||||
subscriptMap := map[rune]string{
|
|
||||||
'0': "₀", '1': "₁", '2': "₂", '3': "₃", '4': "₄",
|
|
||||||
'5': "₅", '6': "₆", '7': "₇", '8': "₈", '9': "₉",
|
|
||||||
'a': "ₐ", 'e': "ₑ", 'h': "ₕ", 'i': "ᵢ", 'j': "ⱼ",
|
|
||||||
'k': "ₖ", 'l': "ₗ", 'm': "ₘ", 'n': "ₙ", 'o': "ₒ",
|
|
||||||
'p': "ₚ", 'r': "ᵣ", 's': "ₛ", 't': "ₜ", 'u': "ᵤ",
|
|
||||||
'v': "ᵥ", 'x': "ₓ",
|
|
||||||
'+': "₊", '-': "₋", '=': "₌", '(': "₍", ')': "₎",
|
|
||||||
}
|
|
||||||
|
|
||||||
result := text
|
|
||||||
|
|
||||||
// Handle _{...} format
|
|
||||||
for {
|
|
||||||
start := strings.Index(result, "_{")
|
|
||||||
if start == -1 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
contentStart := start + 2
|
|
||||||
contentEnd, content := findBracedContent(result, contentStart)
|
|
||||||
if contentEnd == -1 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert content to subscript
|
|
||||||
var subscript strings.Builder
|
|
||||||
for _, ch := range content {
|
|
||||||
if sub, ok := subscriptMap[ch]; ok {
|
|
||||||
subscript.WriteString(sub)
|
|
||||||
} else {
|
|
||||||
// If no subscript version exists, wrap in parentheses
|
|
||||||
subscript.WriteRune(ch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result = result[:start] + subscript.String() + result[contentEnd:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle simple _x format (single character without braces)
|
|
||||||
for i := 0; i < len(result)-1; i++ {
|
|
||||||
if result[i] == '_' && result[i+1] != '{' {
|
|
||||||
ch := rune(result[i+1])
|
|
||||||
if sub, ok := subscriptMap[ch]; ok {
|
|
||||||
result = result[:i] + sub + result[i+2:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m uiModel) renderItem(item *model.Item, isCursor bool) string {
|
func (m uiModel) renderItem(item *model.Item, isCursor bool) string {
|
||||||
var b strings.Builder
|
var b strings.Builder
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue