mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-14 06:03:45 +01:00
[web] emit event when synctex mapping was downloaded from clsi-cache (#25424)
* [clsi] tell frontend when synctex mapping was downloaded from clsi-cache * [web] emit event when synctex mapping was downloaded from clsi-cache GitOrigin-RevId: 1f6b7e0faaa7dd76449aad566802da971a4cf9ed
This commit is contained in:
@@ -204,12 +204,13 @@ function syncFromCode(req, res, next) {
|
||||
line,
|
||||
column,
|
||||
{ imageName, editorId, buildId, compileFromClsiCache },
|
||||
function (error, pdfPositions) {
|
||||
function (error, pdfPositions, downloadedFromCache) {
|
||||
if (error) {
|
||||
return next(error)
|
||||
}
|
||||
res.json({
|
||||
pdf: pdfPositions,
|
||||
downloadedFromCache,
|
||||
})
|
||||
}
|
||||
)
|
||||
@@ -229,12 +230,13 @@ function syncFromPdf(req, res, next) {
|
||||
h,
|
||||
v,
|
||||
{ imageName, editorId, buildId, compileFromClsiCache },
|
||||
function (error, codePositions) {
|
||||
function (error, codePositions, downloadedFromCache) {
|
||||
if (error) {
|
||||
return next(error)
|
||||
}
|
||||
res.json({
|
||||
code: codePositions,
|
||||
downloadedFromCache,
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
@@ -23,6 +23,7 @@ const {
|
||||
downloadLatestCompileCache,
|
||||
downloadOutputDotSynctexFromCompileCache,
|
||||
} = require('./CLSICacheHandler')
|
||||
const { callbackifyMultiResult } = require('@overleaf/promise-utils')
|
||||
|
||||
const COMPILE_TIME_BUCKETS = [
|
||||
// NOTE: These buckets are locked in per metric name.
|
||||
@@ -447,12 +448,20 @@ async function syncFromCode(projectId, userId, filename, line, column, opts) {
|
||||
'-o',
|
||||
outputFilePath,
|
||||
]
|
||||
const stdout = await _runSynctex(projectId, userId, command, opts)
|
||||
const { stdout, downloadedFromCache } = await _runSynctex(
|
||||
projectId,
|
||||
userId,
|
||||
command,
|
||||
opts
|
||||
)
|
||||
logger.debug(
|
||||
{ projectId, userId, filename, line, column, command, stdout },
|
||||
'synctex code output'
|
||||
)
|
||||
return SynctexOutputParser.parseViewOutput(stdout)
|
||||
return {
|
||||
codePositions: SynctexOutputParser.parseViewOutput(stdout),
|
||||
downloadedFromCache,
|
||||
}
|
||||
}
|
||||
|
||||
async function syncFromPdf(projectId, userId, page, h, v, opts) {
|
||||
@@ -465,9 +474,17 @@ async function syncFromPdf(projectId, userId, page, h, v, opts) {
|
||||
'-o',
|
||||
`${page}:${h}:${v}:${outputFilePath}`,
|
||||
]
|
||||
const stdout = await _runSynctex(projectId, userId, command, opts)
|
||||
const { stdout, downloadedFromCache } = await _runSynctex(
|
||||
projectId,
|
||||
userId,
|
||||
command,
|
||||
opts
|
||||
)
|
||||
logger.debug({ projectId, userId, page, h, v, stdout }, 'synctex pdf output')
|
||||
return SynctexOutputParser.parseEditOutput(stdout, baseDir)
|
||||
return {
|
||||
pdfPositions: SynctexOutputParser.parseEditOutput(stdout, baseDir),
|
||||
downloadedFromCache,
|
||||
}
|
||||
}
|
||||
|
||||
async function _checkFileExists(dir, filename) {
|
||||
@@ -522,9 +539,10 @@ async function _runSynctex(projectId, userId, command, opts) {
|
||||
return await OutputCacheManager.promises.queueDirOperation(
|
||||
outputDir,
|
||||
/**
|
||||
* @return {Promise<string>}
|
||||
* @return {Promise<{stdout: string, downloadedFromCache: boolean}>}
|
||||
*/
|
||||
async () => {
|
||||
let downloadedFromCache = false
|
||||
try {
|
||||
await _checkFileExists(directory, 'output.synctex.gz')
|
||||
} catch (err) {
|
||||
@@ -535,13 +553,14 @@ async function _runSynctex(projectId, userId, command, opts) {
|
||||
buildId
|
||||
) {
|
||||
try {
|
||||
await downloadOutputDotSynctexFromCompileCache(
|
||||
projectId,
|
||||
userId,
|
||||
editorId,
|
||||
buildId,
|
||||
directory
|
||||
)
|
||||
downloadedFromCache =
|
||||
await downloadOutputDotSynctexFromCompileCache(
|
||||
projectId,
|
||||
userId,
|
||||
editorId,
|
||||
buildId,
|
||||
directory
|
||||
)
|
||||
} catch (err) {
|
||||
logger.warn(
|
||||
{ err, projectId, userId, editorId, buildId },
|
||||
@@ -554,7 +573,7 @@ async function _runSynctex(projectId, userId, command, opts) {
|
||||
}
|
||||
}
|
||||
try {
|
||||
const output = await CommandRunner.promises.run(
|
||||
const { stdout } = await CommandRunner.promises.run(
|
||||
compileName,
|
||||
command,
|
||||
directory,
|
||||
@@ -563,7 +582,10 @@ async function _runSynctex(projectId, userId, command, opts) {
|
||||
{},
|
||||
compileGroup
|
||||
)
|
||||
return output.stdout
|
||||
return {
|
||||
stdout,
|
||||
downloadedFromCache,
|
||||
}
|
||||
} catch (error) {
|
||||
throw OError.tag(error, 'error running synctex', {
|
||||
command,
|
||||
@@ -686,8 +708,14 @@ module.exports = {
|
||||
stopCompile: callbackify(stopCompile),
|
||||
clearProject: callbackify(clearProject),
|
||||
clearExpiredProjects: callbackify(clearExpiredProjects),
|
||||
syncFromCode: callbackify(syncFromCode),
|
||||
syncFromPdf: callbackify(syncFromPdf),
|
||||
syncFromCode: callbackifyMultiResult(syncFromCode, [
|
||||
'codePositions',
|
||||
'downloadedFromCache',
|
||||
]),
|
||||
syncFromPdf: callbackifyMultiResult(syncFromPdf, [
|
||||
'pdfPositions',
|
||||
'downloadedFromCache',
|
||||
]),
|
||||
wordcount: callbackify(wordcount),
|
||||
promises: {
|
||||
doCompileWithLock,
|
||||
|
||||
@@ -109,6 +109,7 @@ Hello world
|
||||
width: 343.71106,
|
||||
},
|
||||
],
|
||||
downloadedFromCache: false,
|
||||
})
|
||||
done()
|
||||
}
|
||||
@@ -146,6 +147,7 @@ Hello world
|
||||
expect(error).to.not.exist
|
||||
expect(result).to.deep.equal({
|
||||
code: [{ file: 'main.tex', line: 3, column: -1 }],
|
||||
downloadedFromCache: false,
|
||||
})
|
||||
done()
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ Hello world
|
||||
width: 343.71106,
|
||||
},
|
||||
],
|
||||
downloadedFromCache: false,
|
||||
})
|
||||
return done()
|
||||
}
|
||||
@@ -87,6 +88,7 @@ Hello world
|
||||
}
|
||||
expect(codePositions).to.deep.equal({
|
||||
code: [{ file: 'main.tex', line: 3, column: -1 }],
|
||||
downloadedFromCache: false,
|
||||
})
|
||||
return done()
|
||||
}
|
||||
|
||||
@@ -410,7 +410,7 @@ describe('CompileController', function () {
|
||||
|
||||
this.CompileManager.syncFromCode = sinon
|
||||
.stub()
|
||||
.yields(null, (this.pdfPositions = ['mock-positions']))
|
||||
.yields(null, (this.pdfPositions = ['mock-positions']), true)
|
||||
this.CompileController.syncFromCode(this.req, this.res, this.next)
|
||||
})
|
||||
|
||||
@@ -430,6 +430,7 @@ describe('CompileController', function () {
|
||||
this.res.json
|
||||
.calledWith({
|
||||
pdf: this.pdfPositions,
|
||||
downloadedFromCache: true,
|
||||
})
|
||||
.should.equal(true)
|
||||
})
|
||||
@@ -451,7 +452,7 @@ describe('CompileController', function () {
|
||||
|
||||
this.CompileManager.syncFromPdf = sinon
|
||||
.stub()
|
||||
.yields(null, (this.codePositions = ['mock-positions']))
|
||||
.yields(null, (this.codePositions = ['mock-positions']), true)
|
||||
this.CompileController.syncFromPdf(this.req, this.res, this.next)
|
||||
})
|
||||
|
||||
@@ -465,6 +466,7 @@ describe('CompileController', function () {
|
||||
this.res.json
|
||||
.calledWith({
|
||||
code: this.codePositions,
|
||||
downloadedFromCache: true,
|
||||
})
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
@@ -35,7 +35,7 @@ describe('CompileManager', function () {
|
||||
build: 1234,
|
||||
},
|
||||
]
|
||||
this.buildId = 'build-id-123'
|
||||
this.buildId = '00000000000-0000000000000000'
|
||||
this.commandOutput = 'Dummy output'
|
||||
this.compileBaseDir = '/compile/dir'
|
||||
this.outputBaseDir = '/output/dir'
|
||||
@@ -61,6 +61,8 @@ describe('CompileManager', function () {
|
||||
},
|
||||
}
|
||||
this.OutputCacheManager = {
|
||||
BUILD_REGEX: /^[0-9a-f]+-[0-9a-f]+$/,
|
||||
CACHE_SUBDIR: 'generated-files',
|
||||
promises: {
|
||||
queueDirOperation: sinon.stub().callsArg(1),
|
||||
saveOutputFiles: sinon
|
||||
@@ -88,9 +90,10 @@ describe('CompileManager', function () {
|
||||
execFile: sinon.stub().yields(),
|
||||
}
|
||||
this.CommandRunner = {
|
||||
canRunSyncTeXInOutputDir: sinon.stub().returns(false),
|
||||
promises: {
|
||||
run: sinon.stub().callsFake((_1, _2, _3, _4, _5, _6, compileGroup) => {
|
||||
if (compileGroup === 'synctex') {
|
||||
if (compileGroup === 'synctex' || compileGroup === 'synctex-output') {
|
||||
return Promise.resolve({ stdout: this.commandOutput })
|
||||
} else {
|
||||
return Promise.resolve({
|
||||
@@ -141,6 +144,12 @@ describe('CompileManager', function () {
|
||||
.withArgs(Path.join(this.compileDir, 'output.synctex.gz'))
|
||||
.resolves(this.fileStats)
|
||||
|
||||
this.CLSICacheHandler = {
|
||||
notifyCLSICacheAboutBuild: sinon.stub(),
|
||||
downloadLatestCompileCache: sinon.stub().resolves(),
|
||||
downloadOutputDotSynctexFromCompileCache: sinon.stub().resolves(),
|
||||
}
|
||||
|
||||
this.CompileManager = SandboxedModule.require(MODULE_PATH, {
|
||||
requires: {
|
||||
'./LatexRunner': this.LatexRunner,
|
||||
@@ -161,11 +170,7 @@ describe('CompileManager', function () {
|
||||
'./LockManager': this.LockManager,
|
||||
'./SynctexOutputParser': this.SynctexOutputParser,
|
||||
'fs/promises': this.fsPromises,
|
||||
'./CLSICacheHandler': {
|
||||
notifyCLSICacheAboutBuild: sinon.stub(),
|
||||
downloadLatestCompileCache: sinon.stub().resolves(),
|
||||
downloadOutputDotSynctexFromCompileCache: sinon.stub().resolves(),
|
||||
},
|
||||
'./CLSICacheHandler': this.CLSICacheHandler,
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -462,12 +467,83 @@ describe('CompileManager', function () {
|
||||
this.compileDir,
|
||||
this.Settings.clsi.docker.image,
|
||||
60000,
|
||||
{}
|
||||
{},
|
||||
'synctex'
|
||||
)
|
||||
})
|
||||
|
||||
it('should return the parsed output', function () {
|
||||
expect(this.result).to.deep.equal(this.records)
|
||||
expect(this.result).to.deep.equal({
|
||||
codePositions: this.records,
|
||||
downloadedFromCache: false,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('from cache in docker', function () {
|
||||
beforeEach(async function () {
|
||||
this.CommandRunner.canRunSyncTeXInOutputDir.returns(true)
|
||||
this.Settings.path.synctexBaseDir
|
||||
.withArgs(`${this.projectId}-${this.userId}`)
|
||||
.returns('/compile')
|
||||
|
||||
const errNotFound = new Error()
|
||||
errNotFound.code = 'ENOENT'
|
||||
this.outputDir = `${this.outputBaseDir}/${this.projectId}-${this.userId}/${this.OutputCacheManager.CACHE_SUBDIR}/${this.buildId}`
|
||||
const filename = Path.join(this.outputDir, 'output.synctex.gz')
|
||||
this.fsPromises.stat
|
||||
.withArgs(this.outputDir)
|
||||
.onFirstCall()
|
||||
.rejects(errNotFound)
|
||||
this.fsPromises.stat
|
||||
.withArgs(this.outputDir)
|
||||
.onSecondCall()
|
||||
.resolves(this.dirStats)
|
||||
this.fsPromises.stat.withArgs(filename).resolves(this.fileStats)
|
||||
this.CLSICacheHandler.downloadOutputDotSynctexFromCompileCache.resolves(
|
||||
true
|
||||
)
|
||||
this.result = await this.CompileManager.promises.syncFromCode(
|
||||
this.projectId,
|
||||
this.userId,
|
||||
this.filename,
|
||||
this.line,
|
||||
this.column,
|
||||
{
|
||||
imageName: 'image',
|
||||
editorId: '00000000-0000-0000-0000-000000000000',
|
||||
buildId: this.buildId,
|
||||
compileFromClsiCache: true,
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it('should run in output dir', function () {
|
||||
const outputFilePath = '/compile/output.pdf'
|
||||
const inputFilePath = `/compile/${this.filename}`
|
||||
expect(this.CommandRunner.promises.run).to.have.been.calledWith(
|
||||
`${this.projectId}-${this.userId}`,
|
||||
[
|
||||
'synctex',
|
||||
'view',
|
||||
'-i',
|
||||
`${this.line}:${this.column}:${inputFilePath}`,
|
||||
'-o',
|
||||
outputFilePath,
|
||||
],
|
||||
this.outputDir,
|
||||
'image',
|
||||
60000,
|
||||
{},
|
||||
'synctex-output'
|
||||
)
|
||||
})
|
||||
|
||||
it('should return the parsed output', function () {
|
||||
expect(this.result).to.deep.equal({
|
||||
codePositions: this.records,
|
||||
downloadedFromCache: true,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -500,7 +576,8 @@ describe('CompileManager', function () {
|
||||
this.compileDir,
|
||||
customImageName,
|
||||
60000,
|
||||
{}
|
||||
{},
|
||||
'synctex'
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -544,7 +621,10 @@ describe('CompileManager', function () {
|
||||
})
|
||||
|
||||
it('should return the parsed output', function () {
|
||||
expect(this.result).to.deep.equal(this.records)
|
||||
expect(this.result).to.deep.equal({
|
||||
pdfPositions: this.records,
|
||||
downloadedFromCache: false,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import { CursorPosition } from '@/features/ide-react/types/cursor-position'
|
||||
import { isValidTeXFile } from '@/main/is-valid-tex-file'
|
||||
import { PdfScrollPosition } from '@/shared/hooks/use-pdf-scroll-position'
|
||||
import { showFileErrorToast } from '@/features/pdf-preview/components/synctex-toasts'
|
||||
import { sendMB } from '@/infrastructure/event-tracking'
|
||||
|
||||
export default function useSynctex(): {
|
||||
syncToPdf: () => void
|
||||
@@ -115,6 +116,12 @@ export default function useSynctex(): {
|
||||
.then(data => {
|
||||
setShowLogs(false)
|
||||
setHighlights(data.pdf)
|
||||
if (data.downloadedFromCache) {
|
||||
sendMB('synctex-downloaded-from-cache', {
|
||||
projectId,
|
||||
method: 'code',
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(debugConsole.error)
|
||||
.finally(() => {
|
||||
@@ -223,6 +230,12 @@ export default function useSynctex(): {
|
||||
.then(data => {
|
||||
const [{ file, line }] = data.code
|
||||
goToCodeLine(file, line)
|
||||
if (data.downloadedFromCache) {
|
||||
sendMB('synctex-downloaded-from-cache', {
|
||||
projectId,
|
||||
method: 'pdf',
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(debugConsole.error)
|
||||
.finally(() => {
|
||||
|
||||
Reference in New Issue
Block a user