Fix config editor syntax highlighting with character parser
All checks were successful
Deploy GSM / deploy (push) Successful in 25s
All checks were successful
Deploy GSM / deploy (push) Successful in 25s
Replace regex-based tokenization with character-by-character parsing to completely avoid issues with numbers in CSS class names. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -84,41 +84,81 @@ export default function HytaleConfigEditor({ token }) {
|
||||
if (!text) return ''
|
||||
|
||||
// Escape HTML first
|
||||
let result = text
|
||||
let escaped = text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
|
||||
// Use placeholder tokens to avoid regex conflicts
|
||||
const tokens = []
|
||||
let tokenIndex = 0
|
||||
// Process character by character to build highlighted output
|
||||
let result = ''
|
||||
let i = 0
|
||||
|
||||
// Replace strings with placeholders
|
||||
result = result.replace(/"([^"\\]|\\.)*"/g, (match) => {
|
||||
const isKey = result.indexOf(match + ':') !== -1
|
||||
const className = isKey ? 'text-blue-400' : 'text-amber-300'
|
||||
tokens.push(`<span class="${className}">${match}</span>`)
|
||||
return `\x00${tokenIndex++}\x00`
|
||||
})
|
||||
|
||||
// Replace booleans and null
|
||||
result = result.replace(/\b(true|false|null)\b/g, (match) => {
|
||||
tokens.push(`<span class="text-purple-400">${match}</span>`)
|
||||
return `\x00${tokenIndex++}\x00`
|
||||
})
|
||||
|
||||
// Replace numbers (only standalone, not inside placeholders)
|
||||
result = result.replace(/(?<!\x00)(-?\d+\.?\d*)(?!\x00)/g, (match) => {
|
||||
if (match === '' || match === '-') return match
|
||||
tokens.push(`<span class="text-cyan-400">${match}</span>`)
|
||||
return `\x00${tokenIndex++}\x00`
|
||||
})
|
||||
|
||||
// Replace brackets
|
||||
result = result.replace(/([{}\[\]])/g, '<span class="text-gray-500">$1</span>')
|
||||
|
||||
// Restore tokens
|
||||
result = result.replace(/\x00(\d+)\x00/g, (_, idx) => tokens[parseInt(idx)])
|
||||
while (i < escaped.length) {
|
||||
// Check for string start
|
||||
if (escaped[i] === '"') {
|
||||
let str = '"'
|
||||
i++
|
||||
while (i < escaped.length && escaped[i] !== '"') {
|
||||
if (escaped[i] === '\\' && i + 1 < escaped.length) {
|
||||
str += escaped[i] + escaped[i + 1]
|
||||
i += 2
|
||||
} else {
|
||||
str += escaped[i]
|
||||
i++
|
||||
}
|
||||
}
|
||||
if (i < escaped.length) {
|
||||
str += '"'
|
||||
i++
|
||||
}
|
||||
// Check if it's a key (followed by colon)
|
||||
let j = i
|
||||
while (j < escaped.length && /\s/.test(escaped[j])) j++
|
||||
const isKey = escaped[j] === ':'
|
||||
const className = isKey ? 'text-blue-400' : 'text-amber-300'
|
||||
result += `<span class="${className}">${str}</span>`
|
||||
}
|
||||
// Check for brackets
|
||||
else if (/[{}\[\]]/.test(escaped[i])) {
|
||||
result += `<span class="text-gray-400">${escaped[i]}</span>`
|
||||
i++
|
||||
}
|
||||
// Check for numbers
|
||||
else if (/[-\d]/.test(escaped[i]) && (i === 0 || /[\s,:\[\{]/.test(escaped[i-1]))) {
|
||||
let num = ''
|
||||
if (escaped[i] === '-') {
|
||||
num += '-'
|
||||
i++
|
||||
}
|
||||
while (i < escaped.length && /[\d.]/.test(escaped[i])) {
|
||||
num += escaped[i]
|
||||
i++
|
||||
}
|
||||
if (num && num !== '-') {
|
||||
result += `<span class="text-cyan-400">${num}</span>`
|
||||
} else {
|
||||
result += num
|
||||
}
|
||||
}
|
||||
// Check for booleans and null
|
||||
else if (escaped.slice(i, i + 4) === 'true') {
|
||||
result += '<span class="text-purple-400">true</span>'
|
||||
i += 4
|
||||
}
|
||||
else if (escaped.slice(i, i + 5) === 'false') {
|
||||
result += '<span class="text-purple-400">false</span>'
|
||||
i += 5
|
||||
}
|
||||
else if (escaped.slice(i, i + 4) === 'null') {
|
||||
result += '<span class="text-purple-400">null</span>'
|
||||
i += 4
|
||||
}
|
||||
// Regular character
|
||||
else {
|
||||
result += escaped[i]
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user