Created
October 9, 2012 11:17
-
-
Save Teggy/3858038 to your computer and use it in GitHub Desktop.
Live LaTeX previewing with Chocolat and Skim.app
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
* Experimental Chocolat mixins | |
* Copyright(c) 2012 Torsten Grust <[email protected]> | |
*/ | |
var fs = require('fs'); | |
var path = require('path'); | |
var spawn = require('child_process').spawn; | |
var flashLaTeX = require('./tex.js').flashLaTeX; | |
/* Storage keys */ | |
var isActive = '__LaTeX_Flash_active__'; | |
var retryPending = '__LaTeX_Flash_pending__'; | |
var isTeXing = '__LaTeX_Flash_texing__'; | |
/* retry TeXing after this many ms */ | |
var retryInterval = 100; | |
/* LaTeX Flash working directory */ | |
var flashWorkingDirectory = '.flashchoc'; | |
/* toggle Flash LaTeX, initialization */ | |
Hooks.addMenuItem('Actions/Teggy/Start LaTeX Flash', 'control-option-command-f', | |
function() { | |
var doc = Document.current(), | |
transient = Storage.transient(); | |
/* only continue for LaTeX buffers */ | |
if ('latex.tex.text'.indexOf(doc.rootScope())) | |
return; | |
/* only continue if buffer is associated with a file */ | |
if (!doc.path()) | |
return; | |
var texDirectory = path.dirname(doc.path()); /* directory component of LaTeX source */ | |
transient.set(isTeXing, false); | |
/* toggle LaTeX flash activity */ | |
var active = !transient.get(isActive); | |
transient.set(isActive, active); | |
Alert.notify({ | |
title: 'LaTeX Flash', | |
subtitle: active ? 'is running' : 'has been stopped', | |
body: 'Working directory is ' + texDirectory | |
}); | |
if (!active) | |
return; | |
/* establish LaTeX Flash working directory if needed */ | |
var flashDirectory = path.join(texDirectory, flashWorkingDirectory); | |
if (!(fs.existsSync(flashDirectory) && | |
fs.statSync(flashDirectory).isDirectory())) { | |
fs.mkdirSync(flashDirectory); | |
} | |
/* first trigger LaTeX format recompilation, then flash for the first time */ | |
flash(true); | |
flash(false); | |
}) | |
/* trigger on-the-fly LaTeX recompilation */ | |
function flash(ini) { | |
var doc = Document.current(), | |
transient = Storage.transient(), | |
error; | |
/* only continue if LaTeX Flash is active */ | |
if (!transient.get(isActive)) | |
return null; | |
/* only continue for LaTeX buffers */ | |
if ('latex.tex.text'.indexOf(doc.rootScope())) | |
return null; | |
/* only continue if buffer is associated with a file */ | |
if (!doc.path()) | |
return null; | |
var texDirectory = path.dirname(doc.path()); /* directory component of LaTeX source */ | |
/* only continue if no other TeX run is active */ | |
if (transient.get(isTeXing)) | |
{ | |
if (transient.get(retryPending)) | |
return null; | |
transient.set(retryPending, true); | |
/* retry in a little while ... */ | |
setTimeout(function() { transient.set(retryPending, false); flash(ini); }, | |
retryInterval); | |
return null; | |
} | |
Recipe.run(function(recipe) { | |
var lineNo, | |
error; | |
lineNo = ini ? | |
0 : | |
recipe.lineIndexesForCharacterRange(recipe.selection).location + 1; | |
flashLaTeX(transient, doc.text, texDirectory, lineNo); | |
}) | |
} | |
/* Flash LaTeX support: on-the-fly recompilation | |
(initial code written during ICFP 2012, Copenhagen) | |
*/ | |
Hooks.onInsertText(function(c, editor) { | |
/* trigger LaTeX recompilation */ | |
flash(false); | |
}) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
* Chocolate mixin for LaTeX Flash | |
* Copyright(c) 2012 Torsten Grust <[email protected]> | |
* | |
* Initial code written during ICFP 2012, Copenhagen | |
*/ | |
/* spawning the TeX process */ | |
var child_process = require('child_process'); | |
/* file I/O */ | |
var fs = require('fs'); | |
/* path handling */ | |
var path = require('path'); | |
/* Haskell-style array processing */ | |
var prelude = require('./lib/prelude'); | |
/* LaTeX Flash working directory */ | |
var flashWorkingDirectory = '.flashchoc'; | |
var isTeXing = '__LaTeX_Flash_texing__'; | |
var flashLaTeX; | |
exports.flashLaTeX = flashLaTeX = function(transient, source, texDirectory, lineNo) { | |
var flashDirectory = path.join(texDirectory, flashWorkingDirectory); | |
var preamble_body = source.split('\\begin{document}'), | |
preamble = preamble_body[0], | |
body = preamble_body[1]; | |
var begin_document = prelude.lines(preamble).length, | |
inside_preamble = lineNo < begin_document; | |
if (inside_preamble) { | |
preamble = preamble + | |
'\\usepackage{flashmate}' + | |
'\\latexdump' + | |
'\\begin{document}\\end{document}'; | |
fs.writeFileSync(path.join(flashDirectory, 'preamble.tex'), preamble); | |
if (transient.get(isTeXing)) | |
return; | |
transient.set(isTeXing, true); | |
return child_process.execFile( | |
'/usr/texbin/pdfetex', | |
['-ini', | |
'-jobname=choc', | |
'-output-directory', flashDirectory, | |
'-interaction', 'batchmode', | |
'&pdflatex', | |
'\\input', path.join(flashDirectory, 'preamble') | |
], | |
{ cwd: texDirectory, | |
timeout: 10000 | |
}, | |
function(error, stdout, stderr) { | |
transient.set(isTeXing, false); | |
if (!error) { | |
body = '{\\Large\\ttfamily Recompiling \\LaTeX{} preamble\\dots}\n'; | |
runLaTeXviewPDF(transient, body, texDirectory, 1); | |
} | |
}); | |
} | |
else { | |
var cursor = lineNo - begin_document, /* cursor = 0 if on \begin{document} line */ | |
sections = body.split(/\\section\{|\\chapter\{/); /* array of sections */ | |
var section_breaks = | |
prelude.scanl(function(b, s) { return b + prelude.lines(s).length - 1}, | |
0, | |
sections); | |
/* determine top and bottom line of cursor's section */ | |
var section = | |
prelude.head( | |
prelude.filter(function(s) { return (s[0] <= cursor && cursor < s[1]) }, | |
prelude.zip(section_breaks, | |
prelude.drop(1, section_breaks)))); | |
if (section) { | |
var top = prelude.max(0, section[0]), | |
bot = section[1]; | |
/* extract body slice of affected section */ | |
body = prelude.unlines(prelude.lines(body).slice(top, bot)); | |
return runLaTeXviewPDF(transient, body, texDirectory, cursor - top); | |
} | |
} | |
} | |
/* compile TeX body and view PDF */ | |
function runLaTeXviewPDF(transient, body, texDirectory, lineNo) { | |
var flashDirectory = path.join(texDirectory, flashWorkingDirectory); | |
fs.writeFileSync(path.join(flashDirectory, 'body.tex'), | |
'\\begin{document}\n' + | |
body + | |
'\n\\end{document}\n'); | |
if (transient.get(isTeXing)) | |
return; | |
transient.set(isTeXing, true); | |
return child_process.execFile( | |
'/usr/texbin/pdfetex', | |
['-synctex=1', | |
'-shell-escape', | |
'-output-format', 'pdf', | |
'-jobname=body', | |
'-output-directory', flashDirectory, | |
'-interaction', 'nonstopmode', | |
'-fmt', path.join(flashDirectory, 'choc'), | |
path.join(flashDirectory, 'body') | |
], | |
{ cwd: texDirectory, | |
timeout: 10000 | |
}, | |
function(error, stdout, stderr) { | |
transient.set(isTeXing, false); | |
if (!error) { | |
child_process.execFile( | |
'/Applications/Skim.app/Contents/SharedSupport/displayline', | |
['-r', '-b', '-g', | |
lineNo.toString(), | |
'body.pdf', | |
'body.tex' | |
], | |
{ cwd: flashDirectory } | |
); | |
} | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment