[898] | 1 | #!/usr/bin/python |
---|
| 2 | |
---|
| 3 | import yaml, codecs, sys, os.path, optparse |
---|
| 4 | |
---|
| 5 | class Style: |
---|
| 6 | |
---|
| 7 | def __init__(self, header=None, footer=None, |
---|
| 8 | tokens=None, events=None, replaces=None): |
---|
| 9 | self.header = header |
---|
| 10 | self.footer = footer |
---|
| 11 | self.replaces = replaces |
---|
| 12 | self.substitutions = {} |
---|
| 13 | for domain, Class in [(tokens, 'Token'), (events, 'Event')]: |
---|
| 14 | if not domain: |
---|
| 15 | continue |
---|
| 16 | for key in domain: |
---|
| 17 | name = ''.join([part.capitalize() for part in key.split('-')]) |
---|
| 18 | cls = getattr(yaml, '%s%s' % (name, Class)) |
---|
| 19 | value = domain[key] |
---|
| 20 | if not value: |
---|
| 21 | continue |
---|
| 22 | start = value.get('start') |
---|
| 23 | end = value.get('end') |
---|
| 24 | if start: |
---|
| 25 | self.substitutions[cls, -1] = start |
---|
| 26 | if end: |
---|
| 27 | self.substitutions[cls, +1] = end |
---|
| 28 | |
---|
| 29 | def __setstate__(self, state): |
---|
| 30 | self.__init__(**state) |
---|
| 31 | |
---|
| 32 | yaml.add_path_resolver(u'tag:yaml.org,2002:python/object:__main__.Style', |
---|
| 33 | [None], dict) |
---|
| 34 | yaml.add_path_resolver(u'tag:yaml.org,2002:pairs', |
---|
| 35 | [None, u'replaces'], list) |
---|
| 36 | |
---|
| 37 | class YAMLHighlight: |
---|
| 38 | |
---|
| 39 | def __init__(self, options): |
---|
| 40 | config = yaml.load(file(options.config, 'rb').read()) |
---|
| 41 | self.style = config[options.style] |
---|
| 42 | if options.input: |
---|
| 43 | self.input = file(options.input, 'rb') |
---|
| 44 | else: |
---|
| 45 | self.input = sys.stdin |
---|
| 46 | if options.output: |
---|
| 47 | self.output = file(options.output, 'wb') |
---|
| 48 | else: |
---|
| 49 | self.output = sys.stdout |
---|
| 50 | |
---|
| 51 | def highlight(self): |
---|
| 52 | input = self.input.read() |
---|
| 53 | if input.startswith(codecs.BOM_UTF16_LE): |
---|
| 54 | input = unicode(input, 'utf-16-le') |
---|
| 55 | elif input.startswith(codecs.BOM_UTF16_BE): |
---|
| 56 | input = unicode(input, 'utf-16-be') |
---|
| 57 | else: |
---|
| 58 | input = unicode(input, 'utf-8') |
---|
| 59 | substitutions = self.style.substitutions |
---|
| 60 | tokens = yaml.scan(input) |
---|
| 61 | events = yaml.parse(input) |
---|
| 62 | markers = [] |
---|
| 63 | number = 0 |
---|
| 64 | for token in tokens: |
---|
| 65 | number += 1 |
---|
| 66 | if token.start_mark.index != token.end_mark.index: |
---|
| 67 | cls = token.__class__ |
---|
| 68 | if (cls, -1) in substitutions: |
---|
| 69 | markers.append([token.start_mark.index, +2, number, substitutions[cls, -1]]) |
---|
| 70 | if (cls, +1) in substitutions: |
---|
| 71 | markers.append([token.end_mark.index, -2, number, substitutions[cls, +1]]) |
---|
| 72 | number = 0 |
---|
| 73 | for event in events: |
---|
| 74 | number += 1 |
---|
| 75 | cls = event.__class__ |
---|
| 76 | if (cls, -1) in substitutions: |
---|
| 77 | markers.append([event.start_mark.index, +1, number, substitutions[cls, -1]]) |
---|
| 78 | if (cls, +1) in substitutions: |
---|
| 79 | markers.append([event.end_mark.index, -1, number, substitutions[cls, +1]]) |
---|
| 80 | markers.sort() |
---|
| 81 | markers.reverse() |
---|
| 82 | chunks = [] |
---|
| 83 | position = len(input) |
---|
| 84 | for index, weight1, weight2, substitution in markers: |
---|
| 85 | if index < position: |
---|
| 86 | chunk = input[index:position] |
---|
| 87 | for substring, replacement in self.style.replaces: |
---|
| 88 | chunk = chunk.replace(substring, replacement) |
---|
| 89 | chunks.append(chunk) |
---|
| 90 | position = index |
---|
| 91 | chunks.append(substitution) |
---|
| 92 | chunks.reverse() |
---|
| 93 | result = u''.join(chunks) |
---|
| 94 | if self.style.header: |
---|
| 95 | self.output.write(self.style.header) |
---|
| 96 | self.output.write(result.encode('utf-8')) |
---|
| 97 | if self.style.footer: |
---|
| 98 | self.output.write(self.style.footer) |
---|
| 99 | |
---|
| 100 | if __name__ == '__main__': |
---|
| 101 | parser = optparse.OptionParser() |
---|
| 102 | parser.add_option('-s', '--style', dest='style', default='ascii', |
---|
| 103 | help="specify the highlighting style", metavar='STYLE') |
---|
| 104 | parser.add_option('-c', '--config', dest='config', |
---|
| 105 | default=os.path.join(os.path.dirname(sys.argv[0]), 'yaml_hl.cfg'), |
---|
| 106 | help="set an alternative configuration file", metavar='CONFIG') |
---|
| 107 | parser.add_option('-i', '--input', dest='input', default=None, |
---|
| 108 | help="set the input file (default: stdin)", metavar='FILE') |
---|
| 109 | parser.add_option('-o', '--output', dest='output', default=None, |
---|
| 110 | help="set the output file (default: stdout)", metavar='FILE') |
---|
| 111 | (options, args) = parser.parse_args() |
---|
| 112 | hl = YAMLHighlight(options) |
---|
| 113 | hl.highlight() |
---|
| 114 | |
---|