Skip to content
This repository has been archived by the owner on Apr 5, 2018. It is now read-only.

Commit

Permalink
@2.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
rvagg committed Sep 17, 2013
1 parent 56c60af commit abcb1b5
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 128 deletions.
29 changes: 15 additions & 14 deletions README.md
@@ -1,9 +1,12 @@
# Pygmentize (Bundled)

A simple wrapper around Python's Pygments code formatter, with Pygments bundled.
**Python's Pygments code formatter, for Node.js, distributed with Pygments**

Available as a simple *String-in, Buffer-out* interface and also as a *read/write-Stream* interface.
[![NPM](https://nodei.co/npm/pygmentize-bundled.png?downloads=true&stars=true)](https://nodei.co/npm/pygmentize-bundled/) [![NPM](https://nodei.co/npm-dl/pygmentize-bundled.png?months=6)](https://nodei.co/npm/pygmentize-bundled/)

Can be used as either a *String-in, Buffer-out*, or as a Duplex stream.

Compatible with both Python v2 and v3.

## API

Expand All @@ -17,19 +20,18 @@ Pygmentize a given `code` string and return it as a Buffer to the `callback` Fun

**pygmentize(options)**

When you only supply the `options` argument, it will return a read/write Stream that you can pipe to and from to format your code.
When you only supply the `options` argument, it will return a Duplex stream that you can pipe to and from to format your code.

* `options` contains options to be passed to Pygments (see [Options](#options)).


## Options

Language/lexer, formatter, and their options are currently supported. Filters are not supported yet.

* `lang`: source language/lexer name - `string`
* `format`: output formatter name - `string`
* `options`: lexer and formatter options - `object`

* `lang`: source language/lexer name - `String`
* `format`: output formatter name - `String`
* `python`: the full path to the `python` command on the current system, defaults to `'python'` - `String`
* `options`: lexer and formatter options, each key/value pair is passed through to `pygmentize` with `-P` - `Object`

## Examples

Expand Down Expand Up @@ -65,19 +67,18 @@ pygmentize({ lang: 'php', format: 'html', options: { startinline: 1 } }, 'var a
})
```

A streaming API is also available. Simply omit the `code` and `callback` arguments:
A duplex streaming API is also available. Simply omit the `code` and `callback` arguments:

```js
var pygmentize = require('pygmentize-bundled')

process.stdin.pipe(
pygmentize({ lang: 'js', format: 'html' })
).pipe(process.stdout);
process.stdin
.pipe(pygmentize({ lang: 'js', format: 'html' }))
.pipe(process.stdout);
```

Refer to the [Pygments documentation](http://pygments.org/docs/). For supported languages, see the list of [lexers](http://pygments.org/docs/lexers/), for supported formatted, see the list of [formatters](http://pygments.org/docs/formatters/).


## Contributors

* [Rod Vagg](https://github.com/rvagg)
Expand All @@ -89,4 +90,4 @@ Refer to the [Pygments documentation](http://pygments.org/docs/). For supported

Pygments (Bundled) is Copyright (c) 2012 Rod Vagg <@rvagg> and licenced under the MIT licence. All rights not explicitly granted in the MIT license are reserved. See the included LICENSE file for more details.

Pygments is licenced under the BSD licence.
Pygments is licenced under the BSD licence.
248 changes: 142 additions & 106 deletions index.js
@@ -1,111 +1,147 @@
var spawn = require('child_process').spawn
, path = require('path')
, Stream = require('stream').Stream

, defaultFormat = 'html'
, defaultLang = 'js'
, defaultEncoding = 'utf8'

, fromString = function (exec, code, callback) {
var stdout = []
, stderr = ''
, ec = 0
, exitClose = function () {
if (++ec < 2) return

var buf = new Buffer(stdout.reduce(function (p, c) { return p + c.length }, 0))
, i = 0

stdout.forEach(function (s) {
s.copy(buf, i, 0, s.length)
i += s.length
})

callback(null, buf)
}

exec.stdout.on('data', function (data) {
stdout.push(data)
})

exec.stderr.on('data', function (data) {
stderr += data.toString()
})

exec.on('exit', function (code) {
if (code !== 0) {
ec = -1
return callback('Error: ' + stderr)
}
exitClose()
})
exec.on('close', exitClose)

exec.stdin.write(code)
exec.stdin.end()
}

, fromStream = function (exec) {
var stream = new Stream()
, stderr = ''

stream.writable = true
stream.readable = true

exec.stdout.on('data', function (data) {
stream.emit('data', data)
})

exec.stderr.on('data', function (data) {
stderr += data.toString()
})

exec.on('exit', function (code) {
if (code !== 0) {
stream.emit('error', stderr)
} else {
stream.emit('end')
}
})

stream.write = function (data) {
exec.stdin.write(data)
const spawn = require('child_process').spawn
, exec = require('child_process').exec
, path = require('path')
, fs = require('fs')
, PassThrough = require('readable-stream/passthrough')
, mkdirp = require('mkdirp')
, bl = require('bl')
, through2 = require('through2')

, confDir = path.join(process.env.HOME || process.env.USERPROFILE, '.config')
, confFile = path.join(confDir, 'node-pygmentize-bundled.json')

, defaultFormat = 'html'
, defaultLang = 'js'
, defaultEncoding = 'utf8'

var pythonVersions

function fromString (child, code, callback) {
var stdout = bl()
, stderr = bl()
, ec = 0
, exitClose = function () {
if (++ec < 2)
return

callback(null, stdout.slice())
}

stream.end = function () {
exec.stdin.end()
}
child.stdout.pipe(stdout)
child.stderr.pipe(stderr)

stream.destroy = function () {
stream.emit('close')
}

return stream
}

, pygmentize = function (options, code, callback) {
options = options || {}

var execArgs = [
'-f', options.format || defaultFormat
, '-l', options.lang || defaultLang
, '-P', 'encoding=' + (options.encoding || defaultEncoding)
]

if (options.options) {
for (var option in options.options) {
execArgs.push('-P', option + '=' + options.options[option])
}
}

var exec = spawn(
typeof options.python == 'string' ? options.python : 'python'
, [ path.join(__dirname, 'vendor/pygments/pygmentize') ].concat(execArgs)
)

return typeof code == 'string' && typeof callback == 'function'
? fromString(exec, code, callback)
: fromStream(exec)
child.on('exit', function (code) {
if (code !== 0) {
ec = -1
return callback('Error: ' + stderr)
}
exitClose()
})
child.on('close', exitClose)

child.stdin.write(code)
child.stdin.end()
}

function fromStream (retStream, intStream, child) {
var stderr = bl()
, outStream = through2(function (chunk, enc, callback) {
retStream.__write(chunk, enc, callback)
})

module.exports = pygmentize
intStream.pipe(child.stdin)
child.stdout.pipe(outStream)
child.stderr.pipe(stderr)

child.on('exit', function (code) {
if (code !== 0)
retStream.emit('error', stderr.toString())
retStream.__end()
})
}

function pygmentize (options, code, callback) {
options = options || {}

var execArgs = [
'-f', options.format || defaultFormat
, '-l', options.lang || defaultLang
, '-P', 'encoding=' + (options.encoding || defaultEncoding)
]
, toString = typeof code == 'string' && typeof callback == 'function'
, retStream = !toString && through2()
, intStream = !toString && through2()

if (typeof options.options == 'object') {
Object.keys(options.options).forEach(function (key) {
execArgs.push('-P', key + '=' + options.options[key])
})
}

spawnPygmentize(options, execArgs, function (err, child) {
if (err)
return callback(err)
if (toString)
return fromString(child, code, callback)
fromStream(retStream, intStream, child)
})

if (retStream) {
retStream.__write = retStream.write
retStream.write = intStream.write.bind(intStream)
retStream.__end = retStream.end
retStream.end = intStream.end.bind(intStream)
}

return retStream
}

function spawnPygmentize (options, execArgs, callback) {
var python = typeof options.python == 'string' ? options.python : 'python'

pythonVersion(python, function (err, version) {
if (err)
return callback(err)
if (version != 2 && version != 3)
return callback(new Error('Unsupported Python version: ' + version))

var pyg = path.join(
__dirname
, 'vendor/pygments'
, version == 2 ? 'build-2.7' : 'build-3.3'
, 'pygmentize'
)

callback(null, spawn(python, [ pyg ].concat(execArgs)))
})
}

function pythonVersion (python, callback) {
if (!pythonVersions) {
try {
pythonVersions = require(confFile)
} catch (e) {}
if (typeof pythonVersions != 'object')
pythonVersions = {}
}

if (pythonVersions[python])
return callback(null, pythonVersions[python])

exec(python + ' -V', function (err, stdout, stderr) {
if (err)
return callback(err)

var m = stderr.toString().match(/^Python (\d)[.\d]+/i)
if (!m)
return callback(new Error('cannot determine Python version: [' + stderr.toString() + ']'))

pythonVersions[python] = +m[1]

mkdirp.sync(confDir)
fs.writeFileSync(confFile, JSON.stringify(pythonVersions), 'utf8')
return callback(null, +m[1])
})
}

module.exports = pygmentize
18 changes: 12 additions & 6 deletions package.json
@@ -1,14 +1,14 @@
{
"name" : "pygmentize-bundled"
, "version" : "1.2.0"
, "version" : "2.0.0"
, "description" : "A simple wrapper around Python's Pygments code formatter, with Pygments bundled"
, "main" : "index.js"
, "scripts" : {
"test" : "node ./test/test.js"
"test" : "node ./test.js"
}
, "repository" : {
"type" : "git"
, "url" : "https://github.com/rvagg/node-pygmentize-bundled.git"
"type" : "git"
, "url" : "https://github.com/rvagg/node-pygmentize-bundled.git"
}
, "keywords" : [
"pygments"
Expand All @@ -24,8 +24,14 @@
, "Scott Walkinshaw (https://github.com/swalkinshaw)"
]
, "license" : "MIT"
, "dependencies" : {
"mkdirp" : "~0.3.5"
, "readable-stream" : "~1.0.17"
, "bl" : "~0.4.1"
, "through2" : "~0.2.1"
}
, "devDependencies" : {
"stream-equal" : ">= 0.0.1"
, "tape" : "*"
"stream-equal" : ">= 0.0.1"
, "tape" : "*"
}
}
5 changes: 3 additions & 2 deletions test.js
Expand Up @@ -19,7 +19,7 @@ function simpleStringConversionTest (python) {
, format: 'console'
, input: 'bool a = true;'
, output: '\u001b[36mbool\u001b[39;49;00m \u001b[39;49;00ma\u001b[39;49;00m '
+ '\u001b[39;49;00m=\u001b[39;49;00m \u001b[39;49;00m\u001b[34mtrue\u001b[39;49;00m;\u001b[39;49;00m'
+ '\u001b[39;49;00m=\u001b[39;49;00m \u001b[39;49;00m\u001b[36mtrue\u001b[39;49;00m;\u001b[39;49;00m'
}
, {
lang: 'php'
Expand All @@ -37,7 +37,7 @@ function simpleStringConversionTest (python) {
}
]

t.plan(cases.length * 2)
t.plan(cases.length * 3)

cases.forEach(function (c) {
pygments(
Expand All @@ -50,6 +50,7 @@ function simpleStringConversionTest (python) {
, c.input
, function (err, result) {
t.equal(err, null)
t.ok(Buffer.isBuffer(result), 'isBuffer')
result = result.toString().replace(/\n/g, '')
t.equal(result, c.output)
}
Expand Down

0 comments on commit abcb1b5

Please sign in to comment.