overleaf/services/clsi/app/js/SynctexOutputParser.js
Antoine Clausse 7f48c67512 Add prefer-node-protocol ESLint rule (#21532)
* Add `unicorn/prefer-node-protocol`

* Fix `unicorn/prefer-node-protocol` ESLint errors

* Run `npm run format:fix`

* Add sandboxed-module sourceTransformers in mocha setups

Fix `no such file or directory, open 'node:fs'` in `sandboxed-module`

* Remove `node:` in the SandboxedModule requires

* Fix new linting errors with `node:`

GitOrigin-RevId: 68f6e31e2191fcff4cb8058dd0a6914c14f59926
2024-11-11 09:04:51 +00:00

113 lines
2.7 KiB
JavaScript

const Path = require('node:path')
/**
* Parse output from the `synctex view` command
*/
function parseViewOutput(output) {
return _parseOutput(output, (record, label, value) => {
switch (label) {
case 'Page':
_setIntProp(record, 'page', value)
break
case 'h':
_setFloatProp(record, 'h', value)
break
case 'v':
_setFloatProp(record, 'v', value)
break
case 'W':
_setFloatProp(record, 'width', value)
break
case 'H':
_setFloatProp(record, 'height', value)
break
}
})
}
/**
* Parse output from the `synctex edit` command
*/
function parseEditOutput(output, baseDir) {
return _parseOutput(output, (record, label, value) => {
switch (label) {
case 'Input':
if (Path.isAbsolute(value)) {
record.file = Path.relative(baseDir, value)
} else {
record.file = value
}
break
case 'Line':
_setIntProp(record, 'line', value)
break
case 'Column':
_setIntProp(record, 'column', value)
break
}
})
}
/**
* Generic parser for synctex output
*
* Parses the output into records. Each line is split into a label and a value,
* which are then sent to `processLine` for further processing.
*/
function _parseOutput(output, processLine) {
const lines = output.split('\n')
let currentRecord = null
const records = []
for (const line of lines) {
const [label, value] = _splitLine(line)
// A line that starts with 'Output:' indicates a new record
if (label === 'Output') {
// Start new record
currentRecord = {}
records.push(currentRecord)
continue
}
// Ignore the line if we're not in a record yet
if (currentRecord == null) {
continue
}
// Process the line
processLine(currentRecord, label, value)
}
return records
}
/**
* Split a line in label and value components.
*
* The components are separated by a colon. Note that this is slightly
* different from `line.split(':', 2)`. This version puts the entirety of the
* line after the colon in the value component, even if there are more colons
* on the line.
*/
function _splitLine(line) {
const splitIndex = line.indexOf(':')
if (splitIndex === -1) {
return ['', line]
}
return [line.slice(0, splitIndex).trim(), line.slice(splitIndex + 1).trim()]
}
function _setIntProp(record, prop, value) {
const intValue = parseInt(value, 10)
if (!isNaN(intValue)) {
record[prop] = intValue
}
}
function _setFloatProp(record, prop, value) {
const floatValue = parseFloat(value)
if (!isNaN(floatValue)) {
record[prop] = floatValue
}
}
module.exports = { parseViewOutput, parseEditOutput }