mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #14934 from overleaf/revert-14926-revert-14121-bg-best-allow-underscore-in-hyperref-labels
Revert "Revert "allow underscore in hyperref labels"" GitOrigin-RevId: f7b2dd418fa9c0940b778604ed08eccab78f97d2
This commit is contained in:
parent
6ffaeb7a92
commit
45ca0f796c
2 changed files with 141 additions and 5 deletions
|
@ -271,7 +271,7 @@ const read1name = function (TokeniseResult, k) {
|
|||
// handle names like FOO_BAR
|
||||
let delimiterName = ''
|
||||
let j, tok
|
||||
for (j = k + 2, tok; (tok = Tokens[j]); j++) {
|
||||
for (j = k + 2; (tok = Tokens[j]); j++) {
|
||||
if (tok[1] === 'Text') {
|
||||
const str = text.substring(tok[2], tok[3])
|
||||
if (!str.match(/^\S*$/)) {
|
||||
|
@ -302,7 +302,7 @@ const read1filename = function (TokeniseResult, k) {
|
|||
|
||||
let fileName = ''
|
||||
let j, tok
|
||||
for (j = k + 1, tok; (tok = Tokens[j]); j++) {
|
||||
for (j = k + 1; (tok = Tokens[j]); j++) {
|
||||
if (tok[1] === 'Text') {
|
||||
const str = text.substring(tok[2], tok[3])
|
||||
if (!str.match(/^\S*$/)) {
|
||||
|
@ -322,6 +322,54 @@ const read1filename = function (TokeniseResult, k) {
|
|||
}
|
||||
}
|
||||
|
||||
const readOptionalLabel = function (TokeniseResult, k) {
|
||||
// read a label my_label:text..
|
||||
const Tokens = TokeniseResult.tokens
|
||||
const text = TokeniseResult.text
|
||||
|
||||
const params = Tokens[k + 1]
|
||||
|
||||
// Quick check for arguments like [label]
|
||||
if (params && params[1] === 'Text') {
|
||||
const paramNum = text.substring(params[2], params[3])
|
||||
if (paramNum.match(/^(\[[^\]]*\])*\s*$/)) {
|
||||
return k + 1 // got it
|
||||
}
|
||||
}
|
||||
|
||||
let label = ''
|
||||
let j, tok
|
||||
for (j = k + 1; (tok = Tokens[j]); j++) {
|
||||
if (tok[1] === '{') {
|
||||
// unclosed label
|
||||
break
|
||||
} else if (tok[1] === 'Text') {
|
||||
const str = text.substring(tok[2], tok[3])
|
||||
label = label + str
|
||||
if (str.match(/\]/)) {
|
||||
// breaking due to ]
|
||||
break
|
||||
}
|
||||
} else if (tok[1] === '_') {
|
||||
label = label + tok[1]
|
||||
} else {
|
||||
break // breaking due to unrecognised token
|
||||
}
|
||||
}
|
||||
|
||||
if (label.length === 0) {
|
||||
return null
|
||||
} else if (label.length > 0 && /^\[[^\]]*\]\s*$/.test(label)) {
|
||||
// make sure the label is of the form [label]
|
||||
return j - 1 // advance past these tokens
|
||||
} else {
|
||||
// invalid label
|
||||
const e = new Error('Invalid label')
|
||||
e.pos = j + 1
|
||||
return e
|
||||
}
|
||||
}
|
||||
|
||||
const readOptionalParams = function (TokeniseResult, k) {
|
||||
// read an optional parameter [N] where N is a number, used
|
||||
// for \newcommand{\foo}[2]... meaning 2 parameters
|
||||
|
@ -596,20 +644,34 @@ const InterpretTokens = function (TokeniseResult, ErrorReporter) {
|
|||
const nextGroupMathModeStack = [] // tracking all nextGroupMathModes
|
||||
let seenUserDefinedBeginEquation = false // if we have seen macros like \beq
|
||||
let seenUserDefinedEndEquation = false // if we have seen macros like \eeq
|
||||
let seenInfiniteLoop = false // if we have seen an infinite loop in the linter
|
||||
|
||||
// Iterate over the tokens, looking for environments to match
|
||||
//
|
||||
// Push environment command found (\begin, \end) onto the
|
||||
// Environments array.
|
||||
|
||||
for (let i = 0, len = Tokens.length; i < len; i++) {
|
||||
for (let i = 0, len = Tokens.length, lastPos = -1; i < len; i++) {
|
||||
const token = Tokens[i]
|
||||
// const line = token[0]
|
||||
const type = token[1]
|
||||
// const start = token[2]
|
||||
// const end = token[3]
|
||||
const seq = token[4]
|
||||
|
||||
if (i > lastPos) {
|
||||
// advanced successfully through the tokens
|
||||
lastPos = i
|
||||
} else {
|
||||
// we're not moving forward, so force the parsing to advance
|
||||
if (!seenInfiniteLoop)
|
||||
console.error('infinite loop in linter detected', lastPos, i, token)
|
||||
lastPos = lastPos + 1
|
||||
i = lastPos + 1
|
||||
seenInfiniteLoop = true
|
||||
if (i >= len) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if (type === '{') {
|
||||
// handle open group as a type of environment
|
||||
Environments.push({
|
||||
|
@ -667,7 +729,7 @@ const InterpretTokens = function (TokeniseResult, ErrorReporter) {
|
|||
if (open && open[1] === '{' && delimiter && delimiter[1] === 'Text') {
|
||||
let delimiterName = ''
|
||||
let j, tok
|
||||
for (j = i + 2, tok; (tok = Tokens[j]); j++) {
|
||||
for (j = i + 2; (tok = Tokens[j]); j++) {
|
||||
if (tok[1] === 'Text') {
|
||||
const str = text.substring(tok[2], tok[3])
|
||||
if (!str.match(/^\S*$/)) {
|
||||
|
@ -973,6 +1035,30 @@ const InterpretTokens = function (TokeniseResult, ErrorReporter) {
|
|||
i = newPos
|
||||
}
|
||||
nextGroupMathMode = false
|
||||
} else if (seq === 'hyperref') {
|
||||
// try to read any optional params [LABEL].... allowing for
|
||||
// underscores, advance if found
|
||||
let newPos = readOptionalLabel(TokeniseResult, i)
|
||||
if (newPos instanceof Error) {
|
||||
TokenErrorFromTo(
|
||||
Tokens[i + 1],
|
||||
Tokens[Math.min(newPos.pos, len - 1)],
|
||||
'invalid hyperref label'
|
||||
)
|
||||
i = newPos.pos
|
||||
} else if (newPos == null) {
|
||||
/* do nothing */
|
||||
} else {
|
||||
i = newPos
|
||||
}
|
||||
// try to read parameter {....}, advance if found
|
||||
newPos = readDefinition(TokeniseResult, i)
|
||||
if (newPos === null) {
|
||||
/* do nothing */
|
||||
} else {
|
||||
i = newPos
|
||||
}
|
||||
nextGroupMathMode = false
|
||||
} else if (seq === 'resizebox') {
|
||||
// try to read any optional params [BAR]...., advance if found
|
||||
let newPos = readOptionalGeneric(TokeniseResult, i)
|
||||
|
|
|
@ -444,6 +444,56 @@ describe('LatexLinter', function () {
|
|||
assert.equal(errors.length, 0)
|
||||
})
|
||||
|
||||
it('should accept a plain hyperref command', function () {
|
||||
const { errors } = Parse('\\hyperref{http://www.overleaf.com/}')
|
||||
assert.equal(errors.length, 0)
|
||||
})
|
||||
|
||||
it('should accept a hyperref command with underscores in the url ', function () {
|
||||
const { errors } = Parse('\\hyperref{http://www.overleaf.com/my_page.html}')
|
||||
assert.equal(errors.length, 0)
|
||||
})
|
||||
|
||||
it('should accept a hyperref command with category, name and text arguments ', function () {
|
||||
const { errors } = Parse(
|
||||
'\\hyperref{http://www.overleaf.com/}{category}{name}{text}'
|
||||
)
|
||||
assert.equal(errors.length, 0)
|
||||
})
|
||||
|
||||
it('should accept an underscore in a hyperref label', function () {
|
||||
const { errors } = Parse('\\hyperref[foo_bar]{foo bar}')
|
||||
assert.equal(errors.length, 0)
|
||||
})
|
||||
|
||||
it('should reject a $ in a hyperref label', function () {
|
||||
const { errors } = Parse('\\hyperref[foo$bar]{foo bar}')
|
||||
assert.equal(errors.length, 1)
|
||||
})
|
||||
|
||||
it('should reject an unclosed hyperref label', function () {
|
||||
const { errors } = Parse('\\hyperref[foo_bar{foo bar}')
|
||||
assert.equal(errors.length, 2)
|
||||
assert.equal(errors[0].text, 'invalid hyperref label')
|
||||
assert.equal(errors[1].text, 'unexpected close group }')
|
||||
})
|
||||
|
||||
it('should accept a hyperref command without an optional argument', function () {
|
||||
const { errors } = Parse('{\\hyperref{hello}}')
|
||||
assert.equal(errors.length, 0)
|
||||
})
|
||||
|
||||
it('should accept a hyperref command without an optional argument and multiple other arguments', function () {
|
||||
const { errors } = Parse('{\\hyperref{}{}{fig411}}')
|
||||
assert.equal(errors.length, 0)
|
||||
})
|
||||
|
||||
it('should accept a hyperref command without an optional argument in an unclosed group', function () {
|
||||
const { errors } = Parse('{\\hyperref{}{}{fig411}')
|
||||
assert.equal(errors.length, 1)
|
||||
assert.equal(errors[0].text, 'unclosed group {')
|
||||
})
|
||||
|
||||
// %novalidate
|
||||
// %begin novalidate
|
||||
// %end novalidate
|
||||
|
|
Loading…
Reference in a new issue