Added better JSON highlighting

A JSON-specific lexer for Pygments by Norman Richards (@orb)
has been added. It attempts to provide more interesting syntax
highlighting which correctly distinguishes between attribute
names and values.

Closes #25.
This commit is contained in:
Jakub Roztocil 2012-03-13 21:42:18 +01:00
parent 78e20c6e85
commit 31c28807c9
2 changed files with 84 additions and 71 deletions

View File

@ -1,20 +1,19 @@
import os import os
import json import json
import pygments import pygments
import re
from pygments import token from pygments import token
from pygments.util import ClassNotFound from pygments.util import ClassNotFound
from pygments.lexers import get_lexer_for_mimetype from pygments.lexers import get_lexer_for_mimetype
from pygments.formatters.terminal256 import Terminal256Formatter from pygments.formatters.terminal256 import Terminal256Formatter
from pygments.formatters.terminal import TerminalFormatter from pygments.formatters.terminal import TerminalFormatter
from pygments.lexer import include, RegexLexer, bygroups from pygments.lexer import RegexLexer, bygroups
from pygments.styles import get_style_by_name, STYLE_MAP from pygments.styles import get_style_by_name, STYLE_MAP
from . import solarized from . import solarized
from .pygson import JSONLexer
DEFAULT_STYLE = 'solarized' DEFAULT_STYLE = 'solarized'
AVAILABLE_STYLES = [DEFAULT_STYLE] + STYLE_MAP.keys() AVAILABLE_STYLES = [DEFAULT_STYLE] + STYLE_MAP.keys()
TYPE_JS = 'application/javascript'
FORMATTER = (Terminal256Formatter FORMATTER = (Terminal256Formatter
if '256color' in os.environ.get('TERM', '') if '256color' in os.environ.get('TERM', '')
else TerminalFormatter) else TerminalFormatter)
@ -32,68 +31,6 @@ class HTTPLexer(RegexLexer):
(r'(.*?:)(.+)', bygroups(token.Name, token.String)) (r'(.*?:)(.+)', bygroups(token.Name, token.String))
]} ]}
# Stolen from https://github.com/orb/pygments-json
class JSONLexer(RegexLexer):
name = 'JSON Lexer'
aliases = ['json']
filenames = ['*.json']
mimetypes = []
flags = re.DOTALL
tokens = {
'whitespace': [
(r'\s+', token.Text),
],
# represents a simple terminal value
'simplevalue':[
(r'(true|false|null)\b', token.Keyword.Constant),
(r'-?[0-9]+', token.Number.Integer),
(r'"(\\\\|\\"|[^"])*"', token.String.Double),
],
# the right hand side of an object, after the attribute name
'objectattribute': [
include('value'),
(r':', token.Punctuation),
# comma terminates the attribute but expects more
(r',', token.Punctuation, '#pop'),
# a closing bracket terminates the entire object, so pop twice
(r'}', token.Punctuation, ('#pop', '#pop')),
],
# a json object - { attr, attr, ... }
'objectvalue': [
include('whitespace'),
(r'"(\\\\|\\"|[^"])*"', token.Name.Tag, 'objectattribute'),
(r'}', token.Punctuation, '#pop'),
],
# json array - [ value, value, ... }
'arrayvalue': [
include('whitespace'),
include('value'),
(r',', token.Punctuation),
(r']', token.Punctuation, '#pop'),
],
# a json value - either a simple value or a complex value (object or array)
'value': [
include('whitespace'),
include('simplevalue'),
(r'{', token.Punctuation, 'objectvalue'),
(r'\[', token.Punctuation, 'arrayvalue'),
],
# the root of a json document would be a value
'root': [
include('value'),
],
}
class PrettyHttp(object): class PrettyHttp(object):
@ -108,20 +45,19 @@ class PrettyHttp(object):
return pygments.highlight(content, HTTPLexer(), self.formatter) return pygments.highlight(content, HTTPLexer(), self.formatter)
def body(self, content, content_type): def body(self, content, content_type):
lexer = None
content_type = content_type.split(';')[0] content_type = content_type.split(';')[0]
if 'json' in content_type: if 'json' in content_type:
content_type = TYPE_JS lexer = JSONLexer()
try: try:
# Indent JSON # Indent the JSON data.
content = json.dumps(json.loads(content), content = json.dumps(json.loads(content),
sort_keys=True, indent=4) sort_keys=True, indent=4)
lexer = JSONLexer()
except Exception: except Exception:
pass pass
else: if not lexer:
try: try:
lexer = get_lexer_for_mimetype(content_type) lexer = get_lexer_for_mimetype(content_type)
except ClassNotFound: except ClassNotFound:
return content return content
content = pygments.highlight(content, lexer, self.formatter) return pygments.highlight(content, lexer, self.formatter)
return content

77
httpie/pygson.py Normal file
View File

@ -0,0 +1,77 @@
"""
JSON lexer by Norman Richards
It's already been merged into Pygments but not released yet,
so we are temporarily bundling it with HTTPie.
It can be removed once Pygments > 1.4 has been released.
See <https://github.com/jkbr/httpie/pull/25> for more details.
"""
import re
from pygments import token
from pygments.lexer import RegexLexer, include
class JSONLexer(RegexLexer):
name = 'JSON Lexer'
aliases = ['json']
filenames = ['*.json']
mimetypes = []
flags = re.DOTALL
tokens = {
'whitespace': [
(r'\s+', token.Text),
],
# represents a simple terminal value
'simplevalue':[
(r'(true|false|null)\b', token.Keyword.Constant),
(r'-?[0-9]+', token.Number.Integer),
(r'"(\\\\|\\"|[^"])*"', token.String.Double),
],
# the right hand side of an object, after the attribute name
'objectattribute': [
include('value'),
(r':', token.Punctuation),
# comma terminates the attribute but expects more
(r',', token.Punctuation, '#pop'),
# a closing bracket terminates the entire object, so pop twice
(r'}', token.Punctuation, ('#pop', '#pop')),
],
# a json object - { attr, attr, ... }
'objectvalue': [
include('whitespace'),
(r'"(\\\\|\\"|[^"])*"', token.Name.Tag, 'objectattribute'),
(r'}', token.Punctuation, '#pop'),
],
# json array - [ value, value, ... }
'arrayvalue': [
include('whitespace'),
include('value'),
(r',', token.Punctuation),
(r']', token.Punctuation, '#pop'),
],
# a json value - either a simple value or a complex value (object or array)
'value': [
include('whitespace'),
include('simplevalue'),
(r'{', token.Punctuation, 'objectvalue'),
(r'\[', token.Punctuation, 'arrayvalue'),
],
# the root of a json document would be a value
'root': [
include('value'),
],
}