Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support tap PDF to move to LaTeX on remote file system #4387

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/compile/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ async function afterSuccessfulBuilt(lastStep: Step, skipped: boolean) {
const configuration = vscode.workspace.getConfiguration('latex-workshop', vscode.Uri.file(lastStep.rootFile))
// If the PDF viewer is internal, we call SyncTeX in src/components/viewer.ts.
if (configuration.get('view.pdf.viewer') === 'external' && configuration.get('synctex.afterBuild.enabled')) {
const pdfFile = lw.file.getPdfPath(lastStep.rootFile)
const pdfFile = vscode.Uri.parse(lw.file.getPdfPath(lastStep.rootFile))
logger.log('SyncTex after build invoked.')
lw.locate.synctex.toPDF(undefined, undefined, pdfFile)
}
Expand Down
8 changes: 4 additions & 4 deletions src/core/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export async function view(mode?: 'tab' | 'browser' | 'external' | vscode.Uri) {
if (!pickedRootFile) {
return
}
return lw.viewer.view(lw.file.getPdfPath(pickedRootFile), typeof mode === 'string' ? mode : undefined)
return lw.viewer.view(vscode.Uri.parse(lw.file.getPdfPath(pickedRootFile)), typeof mode === 'string' ? mode : undefined)
}

export function refresh() {
Expand All @@ -93,11 +93,11 @@ export function synctex() {
return
}
const configuration = vscode.workspace.getConfiguration('latex-workshop', lw.root.getWorkspace())
let pdfFile: string | undefined = undefined
let pdfFile: vscode.Uri | undefined = undefined
if (lw.root.subfiles.path && configuration.get('latex.rootFile.useSubFile')) {
pdfFile = lw.file.getPdfPath(lw.root.subfiles.path)
pdfFile = vscode.Uri.parse(lw.file.getPdfPath(lw.root.subfiles.path))
} else if (lw.root.file.path !== undefined) {
pdfFile = lw.file.getPdfPath(lw.root.file.path)
pdfFile = vscode.Uri.parse(lw.file.getPdfPath(lw.root.file.path))
}
lw.locate.synctex.toPDF(undefined, undefined, pdfFile)
}
Expand Down
26 changes: 13 additions & 13 deletions src/locate/synctex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ function parseToPDFList(result: string): SyncTeXRecordToPDFAll[] {
* @param pdfFile - The path of a PDF File compiled from the filePath of args.
* If undefined, it is automatically detected.
*/
function toPDF(args?: {line: number, filePath: string}, forcedViewer: 'auto' | 'tabOrBrowser' | 'external' = 'auto', pdfFile?: string) {
function toPDF(args?: {line: number, filePath: string}, forcedViewer: 'auto' | 'tabOrBrowser' | 'external' = 'auto', pdfFile?: vscode.Uri) {
let line: number
let filePath: string
let character = 0
Expand Down Expand Up @@ -222,7 +222,7 @@ function toPDF(args?: {line: number, filePath: string}, forcedViewer: 'auto' | '
logger.log('No root file found.')
return
}
const targetPdfFile = pdfFile ?? lw.file.getPdfPath(rootFile)
const targetPdfFile = pdfFile ?? vscode.Uri.file(lw.file.getPdfPath(rootFile))
if (active.document.lineCount === line &&
active.document.lineAt(line - 1).text === '') {
line -= 1
Expand All @@ -234,10 +234,10 @@ function toPDF(args?: {line: number, filePath: string}, forcedViewer: 'auto' | '

callSyncTeXToPDF(line, character, filePath, targetPdfFile, configuration.get('synctex.indicator') as 'none' | 'circle' | 'rectangle').then((record) => {
void lw.viewer.locate(targetPdfFile, record)
}).catch(() => {
}).catch(async () => {
try {
logger.log(`Forward with synctex.js from ${filePath} to ${pdfFile} on line ${line}.`)
const record = syncTeXToPDF(line, filePath, targetPdfFile)
const record = await syncTeXToPDF(line, filePath, targetPdfFile)
if (!record) {
return
}
Expand All @@ -264,16 +264,16 @@ function toPDF(args?: {line: number, filePath: string}, forcedViewer: 'auto' | '
* @returns A promise resolving to a SyncTeXRecordToPDF object or a
* SyncTeXRecordToPDF[] object.
*/
function callSyncTeXToPDF(line: number, col: number, filePath: string, pdfFile: string, indicator: 'none' | 'circle' | 'rectangle'): Promise<SyncTeXRecordToPDF>
function callSyncTeXToPDF(line: number, col: number, filePath: string, pdfFile: string, indicator: 'none' | 'circle' | 'rectangle'): Promise<SyncTeXRecordToPDFAll[]>
function callSyncTeXToPDF(line: number, col: number, filePath: string, pdfFile: string, indicator: 'none' | 'circle' | 'rectangle'): Promise<SyncTeXRecordToPDF> | Promise<SyncTeXRecordToPDFAll[]> {
function callSyncTeXToPDF(line: number, col: number, filePath: string, pdfFile: vscode.Uri, indicator: 'none' | 'circle' | 'rectangle'): Promise<SyncTeXRecordToPDF>
function callSyncTeXToPDF(line: number, col: number, filePath: string, pdfFile: vscode.Uri, indicator: 'none' | 'circle' | 'rectangle'): Promise<SyncTeXRecordToPDFAll[]>
function callSyncTeXToPDF(line: number, col: number, filePath: string, pdfFile: vscode.Uri, indicator: 'none' | 'circle' | 'rectangle'): Promise<SyncTeXRecordToPDF> | Promise<SyncTeXRecordToPDFAll[]> {
const configuration = vscode.workspace.getConfiguration('latex-workshop')
const docker = configuration.get('docker.enabled')

const args = ['view', '-i'].concat([
`${line}${indicator === 'rectangle' ? ':0' : `:${col + 1}`}:${docker ? path.basename(filePath) : filePath}`,
'-o',
docker ? path.basename(pdfFile) : pdfFile
docker ? path.basename(pdfFile.fsPath) : pdfFile.fsPath
])

let command = configuration.get('synctex.path') as string
Expand All @@ -287,7 +287,7 @@ function callSyncTeXToPDF(line: number, col: number, filePath: string, pdfFile:
}
const logTag = docker ? 'Docker' : 'SyncTeX'
logger.log(`Forward from ${filePath} to ${pdfFile} on line ${line}.`)
const proc = cs.spawn(command, args, {cwd: path.dirname(pdfFile)})
const proc = cs.spawn(command, args, {cwd: path.dirname(pdfFile.fsPath)})
proc.stdout.setEncoding('utf8')
proc.stderr.setEncoding('utf8')

Expand Down Expand Up @@ -420,7 +420,7 @@ function toPDFFromRef(args: {line: number, filePath: string}) {
* position information.
* @param pdfPath - The path of the PDF file.
*/
async function toTeX(data: Extract<ClientRequest, {type: 'reverse_synctex'}>, pdfPath: string) {
async function toTeX(data: Extract<ClientRequest, {type: 'reverse_synctex'}>, pdfPath: vscode.Uri) {
let record: SyncTeXRecordToTeX

// We only use synctex.js for backward sync as the binary cannot handle CJK encodings #4239.
Expand All @@ -436,7 +436,7 @@ async function toTeX(data: Extract<ClientRequest, {type: 'reverse_synctex'}>, pd
// }
try {
logger.log(`Backward from ${pdfPath} at x=${data.pos[0]}, y=${data.pos[1]} on page ${data.page}.`)
const temp = syncTeXToTeX(data.page, data.pos[0], data.pos[1], pdfPath)
const temp = await syncTeXToTeX(data.page, data.pos[0], data.pos[1], pdfPath)
if (!temp) {
return
}
Expand Down Expand Up @@ -678,7 +678,7 @@ function animateToNotify(editor: vscode.TextEditor, position: vscode.Position) {
* @param pdfFile - The path of the PDF file.
* @param rootFile - The path of the root TeX file.
*/
function syncTeXExternal(line: number, pdfFile: string, rootFile: string) {
function syncTeXExternal(line: number, pdfFile: vscode.Uri, rootFile: string) {
if (!vscode.window.activeTextEditor) {
return
}
Expand All @@ -693,7 +693,7 @@ function syncTeXExternal(line: number, pdfFile: string, rootFile: string) {
if (args) {
args = args.map(arg => {
return replaceArgumentPlaceholders(rootFile, lw.file.tmpDirPath)(arg)
.replace(/%PDF%/g, pdfFile)
.replace(/%PDF%/g, pdfFile.fsPath)
.replace(/%LINE%/g, line.toString())
.replace(/%TEX%/g, texFile)
})
Expand Down
48 changes: 27 additions & 21 deletions src/locate/synctex/worker.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as vscode from 'vscode'
import * as fs from 'fs'
import * as iconv from 'iconv-lite'
import * as path from 'path'
Expand Down Expand Up @@ -91,32 +92,37 @@ function toRect(blocks: any): Rectangle {
}
}

function parseSyncTexForPdf(pdfFile: string): PdfSyncObject | undefined {
const filename = path.basename(pdfFile, path.extname(pdfFile))
const dir = path.dirname(pdfFile)
const synctexFile = path.resolve(dir, filename + '.synctex')
const synctexFileGz = synctexFile + '.gz'
async function parseSyncTexForPdf(pdfFile: vscode.Uri): Promise<PdfSyncObject | undefined> {
const filename = path.basename(pdfFile.fsPath, path.extname(pdfFile.fsPath))
const dir = path.dirname(pdfFile.fsPath)
const synctexFile = vscode.Uri.parse(pdfFile.scheme + "://" + path.resolve(dir, filename + '.synctex'))
const synctexFileGz = vscode.Uri.parse(pdfFile.scheme + "://" + synctexFile.fsPath + '.gz')

if (fs.existsSync(synctexFile)) {
try {
await vscode.workspace.fs.stat(synctexFile);
try {
logger.log(`Parsing .synctex ${synctexFile} .`)
const s = fs.readFileSync(synctexFile, {encoding: 'binary'})
return parseSyncTex(s)
const s = await vscode.workspace.fs.readFile(synctexFile)
return parseSyncTex(new TextDecoder().decode(s))
} catch (e: unknown) {
logger.logError(`Failed parsing .synctex ${synctexFile}:`, e)
}
} else if (fs.existsSync(synctexFileGz)) {
} catch (error) {
try {
logger.log(`Parsing .synctex.gz ${synctexFileGz} .`)
const data = fs.readFileSync(synctexFileGz)
const b = zlib.gunzipSync(data)
const s = b.toString('binary')
return parseSyncTex(s)
} catch (e: unknown) {
logger.logError(`Failed parsing .synctex.gz ${synctexFileGz}:`, e)
await vscode.workspace.fs.stat(synctexFileGz);
try {
logger.log(`Parsing .synctex.gz ${synctexFileGz} .`)
const data = await vscode.workspace.fs.readFile(synctexFileGz)
const b = zlib.gunzipSync(data)
const s = b.toString('binary')
return parseSyncTex(s)
} catch (e: unknown) {
logger.logError(`Failed parsing .synctex.gz ${synctexFileGz}:`, e)
}
} catch (error) {
logger.log(`${synctexFile}, ${synctexFileGz} not found.`)
}
}
logger.log(`${synctexFile}, ${synctexFileGz} not found.`)
return undefined
}

Expand All @@ -142,8 +148,8 @@ function findInputFilePathForward(filePath: string, pdfSyncObject: PdfSyncObject
return
}

function syncTeXToPDF(line: number, filePath: string, pdfFile: string): SyncTeXRecordToPDF | undefined {
const pdfSyncObject = parseSyncTexForPdf(pdfFile)
async function syncTeXToPDF(line: number, filePath: string, pdfFile: vscode.Uri): Promise<SyncTeXRecordToPDF | undefined> {
const pdfSyncObject = await parseSyncTexForPdf(pdfFile)
if (!pdfSyncObject) {
return undefined
}
Expand Down Expand Up @@ -177,8 +183,8 @@ function syncTeXToPDF(line: number, filePath: string, pdfFile: string): SyncTeXR
return { page: blocks1[0].page, x: c1.left + pdfSyncObject.offset.x, y: bottom + pdfSyncObject.offset.y, indicator: true }
}

function syncTeXToTeX(page: number, x: number, y: number, pdfPath: string): SyncTeXRecordToTeX | undefined {
const pdfSyncObject = parseSyncTexForPdf(pdfPath)
async function syncTeXToTeX(page: number, x: number, y: number, pdfPath: vscode.Uri): Promise<SyncTeXRecordToTeX | undefined> {
const pdfSyncObject = await parseSyncTexForPdf(pdfPath)
if (!pdfSyncObject) {
return undefined
}
Expand Down
78 changes: 26 additions & 52 deletions src/preview/viewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,7 @@ function refresh(pdfUri?: vscode.Uri): void {
})
}

async function getUrl(pdfFile: string): Promise<string | undefined> {
const pdfUri = vscode.Uri.file(pdfFile)
if (!await lw.file.exists(pdfUri)) {
logger.log(`Cannot find PDF file ${pdfUri}`)
logger.refreshStatus('check', 'statusBar.foreground', `Cannot view file PDF file. File not found: ${pdfUri}`, 'warning')
return
}
return (await lw.server.getUrl(pdfUri)).url
}

async function view(pdfFile: string, mode?: 'tab' | 'browser' | 'external'): Promise<void> {
async function view(pdfFile: vscode.Uri, mode?: 'tab' | 'browser' | 'external'): Promise<void> {
const configuration = vscode.workspace.getConfiguration('latex-workshop')
const tabEditorGroup = configuration.get('view.pdf.tab.editorGroup') as string
let viewerMode: ViewerMode = mode ?? configuration.get<ViewerMode>('view.pdf.viewer', 'tab')
Expand All @@ -105,24 +95,19 @@ async function view(pdfFile: string, mode?: 'tab' | 'browser' | 'external'): Pro
*
* @param pdfFile The path of a PDF file.
*/
async function viewInBrowser(pdfFile: string): Promise<void> {
const url = await getUrl(pdfFile)
if (!url) {
return
}
const pdfUri = vscode.Uri.file(pdfFile)
manager.create(pdfUri)
lw.watcher.pdf.add(pdfUri)
async function viewInBrowser(pdfFile: vscode.Uri): Promise<void> {
manager.create(pdfFile)
lw.watcher.pdf.add(pdfFile)
try {
logger.log(`Serving PDF file at ${url}`)
await vscode.env.openExternal(vscode.Uri.parse(url, true))
logger.log(`Open PDF viewer for ${pdfUri.toString(true)}`)
logger.log(`Serving PDF file at ${pdfFile.fsPath}`)
await vscode.env.openExternal(pdfFile)
logger.log(`Open PDF viewer for ${pdfFile.toString(true)}`)
} catch (e: unknown) {
void vscode.window.showInputBox({
prompt: 'Unable to open browser. Please copy and visit this link.',
value: url
value: pdfFile.fsPath
})
logger.logError(`Failed opening PDF viewer for ${pdfUri.toString(true)}`, e)
logger.logError(`Failed opening PDF viewer for ${pdfFile.toString(true)}`, e)
}
}

Expand All @@ -133,23 +118,13 @@ async function viewInBrowser(pdfFile: string): Promise<void> {
* @param tabEditorGroup
* @param preserveFocus
*/
async function viewInTab(pdfFile: string, tabEditorGroup: string, preserveFocus: boolean): Promise<void> {
const url = await getUrl(pdfFile)
if (!url) {
return
}
const pdfUri = vscode.Uri.file(pdfFile)
return viewInWebviewPanel(pdfUri, tabEditorGroup, preserveFocus)
async function viewInTab(pdfFile: vscode.Uri, tabEditorGroup: string, preserveFocus: boolean): Promise<void> {
return viewInWebviewPanel(pdfFile, tabEditorGroup, preserveFocus)
}

async function viewInCustomEditor(pdfFile: string): Promise<void> {
const url = await getUrl(pdfFile)
if (!url) {
return
}
async function viewInCustomEditor(pdfFile: vscode.Uri): Promise<void> {
const configuration = vscode.workspace.getConfiguration('latex-workshop')
const editorGroup = configuration.get('view.pdf.tab.editorGroup') as string
const pdfUri = vscode.Uri.file(pdfFile)
const showOptions: vscode.TextDocumentShowOptions = {
viewColumn: vscode.ViewColumn.Active,
preserveFocus: true
Expand All @@ -158,10 +133,10 @@ async function viewInCustomEditor(pdfFile: string): Promise<void> {
const currentColumn = vscode.window.activeTextEditor?.viewColumn
if (currentColumn && currentColumn > 1) {
showOptions.viewColumn = currentColumn - 1
await vscode.commands.executeCommand('vscode.openWith', pdfUri, 'latex-workshop-pdf-hook', showOptions)
await vscode.commands.executeCommand('vscode.openWith', pdfFile, 'latex-workshop-pdf-hook', showOptions)
await vscode.commands.executeCommand('workbench.action.focusRightGroup')
} else {
await vscode.commands.executeCommand('vscode.openWith', pdfUri, 'latex-workshop-pdf-hook', showOptions)
await vscode.commands.executeCommand('vscode.openWith', pdfFile, 'latex-workshop-pdf-hook', showOptions)
if (currentColumn === vscode.ViewColumn.One) {
await moveActiveEditor('left', true)
} else {
Expand All @@ -171,13 +146,13 @@ async function viewInCustomEditor(pdfFile: string): Promise<void> {
} else if (editorGroup === 'right') {
const currentColumn = vscode.window.activeTextEditor?.viewColumn
showOptions.viewColumn = (currentColumn ?? 0) + 1
await vscode.commands.executeCommand('vscode.openWith', pdfUri, 'latex-workshop-pdf-hook', showOptions)
await vscode.commands.executeCommand('vscode.openWith', pdfFile, 'latex-workshop-pdf-hook', showOptions)
await vscode.commands.executeCommand('workbench.action.focusLeftGroup')
} else {
await vscode.commands.executeCommand('vscode.openWith', pdfUri, 'latex-workshop-pdf-hook', showOptions)
await vscode.commands.executeCommand('vscode.openWith', pdfFile, 'latex-workshop-pdf-hook', showOptions)
await moveActiveEditor(editorGroup, true)
}
logger.log(`Open PDF tab for ${pdfUri.toString(true)}`)
logger.log(`Open PDF tab for ${pdfFile.toString(true)}`)
}

async function viewInWebviewPanel(pdfUri: vscode.Uri, tabEditorGroup: string, preserveFocus: boolean): Promise<void> {
Expand Down Expand Up @@ -215,7 +190,7 @@ async function viewInWebviewPanel(pdfUri: vscode.Uri, tabEditorGroup: string, pr
*
* @param pdfFile The path of a PDF file.
*/
function viewInExternal(pdfFile: string): void {
function viewInExternal(pdfFile: vscode.Uri): void {
const configuration = vscode.workspace.getConfiguration('latex-workshop')
let command = configuration.get('view.pdf.external.viewer.command') as string
let args = configuration.get('view.pdf.external.viewer.args') as string[]
Expand All @@ -238,11 +213,11 @@ function viewInExternal(pdfFile: string): void {
}
}
if (args) {
args = args.map(arg => arg.replace('%PDF%', pdfFile))
args = args.map(arg => arg.replace('%PDF%', pdfFile.fsPath))
}
logger.log(`Open external viewer for ${pdfFile}`)
logger.logCommand('Execute the external PDF viewer command', command, args)
const proc = cs.spawn(command, args, {cwd: path.dirname(pdfFile), detached: true})
const proc = cs.spawn(command, args, {cwd: path.dirname(pdfFile.fsPath), detached: true})
let stdout = ''
proc.stdout.on('data', newStdout => {
stdout += newStdout
Expand Down Expand Up @@ -290,13 +265,13 @@ function handler(websocket: ws, msg: string): void {
if (configuration.get('synctex.afterBuild.enabled') as boolean) {
logger.log('SyncTex after build invoked.')
const uri = vscode.Uri.parse(data.pdfFileUri, true)
lw.locate.synctex.toPDF(undefined, undefined, uri.fsPath)
lw.locate.synctex.toPDF(undefined, undefined, uri)
}
break
}
case 'reverse_synctex': {
const uri = vscode.Uri.parse(data.pdfFileUri, true)
void lw.locate.synctex.toTeX(data, uri.fsPath)
void lw.locate.synctex.toTeX(data, uri)
break
}
case 'external_link': {
Expand Down Expand Up @@ -391,19 +366,18 @@ function getParams(): PdfViewerParams {
* @param pdfFile The path of a PDF file.
* @param record The position to be revealed.
*/
async function locate(pdfFile: string, record: SyncTeXRecordToPDF | SyncTeXRecordToPDFAll[]): Promise<void> {
const pdfUri = vscode.Uri.file(pdfFile)
let clientSet = manager.getClients(pdfUri)
async function locate(pdfFile: vscode.Uri, record: SyncTeXRecordToPDF | SyncTeXRecordToPDFAll[]): Promise<void> {
let clientSet = manager.getClients(pdfFile)
if (clientSet === undefined || clientSet.size === 0) {
logger.log(`PDF is not opened: ${pdfFile} , try opening.`)
await view(pdfFile)
clientSet = manager.getClients(pdfUri)
clientSet = manager.getClients(pdfFile)
}
if (clientSet === undefined || clientSet.size === 0) {
logger.log(`PDF cannot be opened: ${pdfFile} .`)
return
}
const needDelay = showInvisibleWebviewPanel(pdfUri)
const needDelay = showInvisibleWebviewPanel(pdfFile)
for (const client of clientSet) {
setTimeout(() => {
client.send({type: 'synctex', data: record})
Expand Down
Loading