Help needed to get pandoc running from init.coffee


#1
Hello all,

I’m trying to build documents with pandoc, and use init.coffee to give me that functionality. I’m typing most of my documents in markdown, and build them to either .docx or LaTeX . I’m now using the “build” feature from Sublime for this, but I’d like to go open source and use Atom instead.

Here is my init.coffee :

atom.commands.add 'atom-text-editor', 'custom:pandoc2Word': ->
  childProcess = require 'child_process'
  cmd = 'pandoc'
  editor = atom.workspace.getActiveTextEditor()
  to_path = ' -o ' + editor.getPath() + '.docx'
  args = [editor.getPath(), "-s", to_path]
  cwd = "/home/victor/"
  console.log("arguments:" + args)
  pandoc = childProcess.spawn cmd, args,  {cwd}

  pandoc.stdout.on 'data', (d) -> console.log('stdout: ' + d);
  pandoc.stderr.on 'data', (d) -> console.log('stderr: ' + d);
  pandoc.on 'close', (c) -> console.log('child process exited with code ' + c);

With Ctrl-Shift-P I see custom:Pandoc2Word, I can run it, and in the console I see the expected arguments printed (arguments:/home/victor/test.markdown,-s, -o /home/victor/test.markdown.docx).

But… Pandoc returns error 1:

stderr: pandoc: -o /home/victor/test.markdown.docx: openFile: does not exist (No such file or directory).

Results of googling led to this page on http://tex.stackexchange.com/, so that’s why I set the value of cwd manually for now.

I’ve also looked at the source of atom-pandoc-preview, but I can’t figure out why that works, and my script doesn’t.

I know this is in between pandoc and Atom, but maybe someone is willing to help.

Tinkerer


#2

Have you tried using the build package? It might make things easier.


#3

Hi! As far as I understood, the build package only works on projects. I’d like to be able to convert a single file to Word and / or LaTeX, occasionally into HTML. That’s what works easily with the build system in Sublime. The build package only does complete projects as far as I can tell from the webpage. I also saw this: https://github.com/noseglid/atom-build/issues/4, but that seems to handle one kind of action for a single-file build, where I sometimes want to make a docx, and sometimes a tex file.


#4

At first glance I would suggest you move -o to args.
You get the No such file or directory message because -o is treated as part of the file name.

to_path = editor.getPath() + '.docx'
args = [editor.getPath(), '-s', '-o', to_path]

should work.


#5

Great!

That worked. Can you maybe tell me how this works (or what to google for)? I thought that everything would just be passed in one go to pandoc, just as I would type it on the command line. How is passing commands through node / coffeescript or any other program different?
I know this is a bit off-topic, but I’d like to understand.


#6

On OS-level there is no command line. A program receives its command line arguments as an array of strings (thats just how program execution works).

When you type your command into bash or whatever shell you’re using, the shell first parses your command line, splits it into a command and an array of arguments and then executes the program with a OS call.
When you want to write a program (or here: an init script for atom) that executes another program then you don’t have the convenience of a shell / command line. Here you are executing the program using the OS call directly, which means you have to follow the rules of the OS.

On Linux the system call in question is called execve and node’s child_process probably uses this function. It takes three parameters: A program to execute, an array of arguments and an array of environment variables. In your case the command to execute is pandoc, it has an array of 4 arguments and the environment variables are handled by child_process.

I hope this explanation is not too complicated, we are talking about low level stuff here :wink: .


#7

Thanks for your explanation, it is completely clear now. Thanks a lot!
I just never thought about how commands were sent to a program. I know it gets separate arguments, but in my mental picture those would just be space-separated things, or the program would parse the command-line or something like that :smile:

Thanks again!


#8

This is what I made for my init.coffee to convert files using Pandoc:


MakePandocFile = (extention, args) ->
  [pandoc_args,cwd] =  MakePandocArgs(extention,args)
  spawnchild('pandoc',pandoc_args,cwd)

MakePandocArgs = (extention, args) ->
  editor = atom.workspace.getActiveTextEditor()
  from_path = editor.getPath()
  to_path = from_path.substr(0, from_path.lastIndexOf('.') + 1) + extention;
  cwd = from_path.substr(0, from_path.lastIndexOf('\\') + 1);
  fpath = [from_path]
  pandoc_args = fpath.concat(args, ["-o"],[to_path])
  [pandoc_args, cwd]

spawnchild = (cmd,args,cwd) ->
  childProcess = require 'child_process'
  pandoc = childProcess.spawn cmd,args, {cwd}
  pandoc.stdout.on 'data', (d) -> console.log('stdout: ' + d);
  pandoc.stderr.on 'data', (d) -> console.log('stderr: ' + d);
  pandoc.on 'close', (c) -> console.log('child process exited with code ' + c);

atom.commands.add 'atom-text-editor', 'Pandoc:pandoc2Word': ->
  args = ['-s']
  MakePandocFile('docx',args)

atom.commands.add 'atom-text-editor', 'Pandoc:pandoc2Tex': ->
  args = ['--latex-engine=xelatex', '-s']
  MakePandocFile('tex',args)

atom.commands.add 'atom-text-editor', 'Pandoc:pandoc2HTML': ->
  args = ['--webtex','-s']
  MakePandocFile('html',args)

atom.commands.add 'atom-text-editor', 'Pandoc:pandoc2RevealJS': ->
  args = ['-t', 'revealjs','-V','theme=solarized','transistion=fade','-s']
  MakePandocFile('html',args)

#9

For those interested: I put the code here, as I could not find time to make a package:


#10

@Tinkerer, Many thanks for sharing your init.coffee, works beautifully for me (Mac). @Tinkerer, @deprint, thanks for getting me onto the learning curve,