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 | |
---|