blob: 55dd27702d6aa8cd4ce8a1a0496617c8551bb43a [file] [log] [blame]
Brett Cannon20e192b2006-03-01 20:53:08 +00001from __future__ import with_statement
2
Brett Cannona4fe1822006-02-25 14:52:53 +00003import keyword
4import exceptions
5import __builtin__
6from string import Template
7
Thomas Wouters89f507f2006-12-13 04:49:30 +00008comment_header = '''" Auto-generated Vim syntax file for Python.
Brett Cannona4fe1822006-02-25 14:52:53 +00009"
Thomas Wouters89f507f2006-12-13 04:49:30 +000010" To use: copy or symlink to ~/.vim/syntax/python.vim'''
Brett Cannona4fe1822006-02-25 14:52:53 +000011
12statement_header = """
13if exists("b:current_syntax")
14 finish
15endif"""
16
17statement_footer = '''
18" Uncomment the 'minlines' statement line and comment out the 'maxlines'
19" statement line; changes behaviour to look at least 2000 lines previously for
20" syntax matches instead of at most 200 lines
21syn sync match pythonSync grouphere NONE "):$"
22syn sync maxlines=200
23"syn sync minlines=2000
24
25let b:current_syntax = "python"'''
26
27looping = ('for', 'while')
28conditionals = ('if', 'elif', 'else')
29boolean_ops = ('and', 'in', 'is', 'not', 'or')
30import_stmts = ('import', 'from')
31object_defs = ('def', 'class')
32
Thomas Wouters89f507f2006-12-13 04:49:30 +000033exception_names = sorted(exc for exc in dir(exceptions)
Brett Cannona4fe1822006-02-25 14:52:53 +000034 if not exc.startswith('__'))
35
36# Need to include functions that start with '__' (e.g., __import__), but
37# nothing that comes with modules (e.g., __name__), so just exclude anything in
38# the 'exceptions' module since we want to ignore exceptions *and* what any
39# module would have
Thomas Wouters89f507f2006-12-13 04:49:30 +000040builtin_names = sorted(builtin for builtin in dir(__builtin__)
Brett Cannona4fe1822006-02-25 14:52:53 +000041 if builtin not in dir(exceptions))
42
43escapes = (r'+\\[abfnrtv\'"\\]+', r'"\\\o\{1,3}"', r'"\\x\x\{2}"',
44 r'"\(\\u\x\{4}\|\\U\x\{8}\)"', r'"\\$"')
45
46todos = ("TODO", "FIXME", "XXX")
47
48# XXX codify?
49numbers = (r'"\<0x\x\+[Ll]\=\>"', r'"\<\d\+[LljJ]\=\>"',
50 '"\.\d\+\([eE][+-]\=\d\+\)\=[jJ]\=\>"',
51 '"\<\d\+\.\([eE][+-]\=\d\+\)\=[jJ]\=\>"',
52 '"\<\d\+\.\d\+\([eE][+-]\=\d\+\)\=[jJ]\=\>"')
53
54contained = lambda x: "%s contained" % x
55
56def str_regexes():
57 """Generator to yield various combinations of strings regexes"""
58 regex_template = Template('matchgroup=Normal ' +
59 'start=+[uU]\=${raw}${sep}+ ' +
60 'end=+${sep}+ ' +
61 '${skip} ' +
62 '${contains}')
63 skip_regex = Template(r'skip=+\\\\\|\\${sep}+')
64 for raw in ('', '[rR]'):
65 for separator in ("'", '"', '"""', "'''"):
66 if len(separator) == 1:
67 skip = skip_regex.substitute(sep=separator)
68 else:
69 skip = ''
Brett Cannonacde7342006-03-01 04:28:00 +000070 contains = 'contains=pythonEscape' if not raw else ''
Brett Cannona4fe1822006-02-25 14:52:53 +000071 yield regex_template.substitute(raw=raw, sep=separator, skip=skip,
72 contains = contains)
73
74space_errors = (r'excludenl "\S\s\+$"ms=s+1', r'" \+\t"', r'"\t\+ "')
75
76statements = (
77 ('',
78 # XXX Might need to change pythonStatement since have
79 # specific Repeat, Conditional, Operator, etc. for 'while',
80 # etc.
81 [("Statement", "pythonStatement", "keyword",
82 (kw for kw in keyword.kwlist
83 if kw not in (looping + conditionals + boolean_ops +
84 import_stmts + object_defs))
85 ),
86 ("Statement", "pythonStatement", "keyword",
87 (' '.join(object_defs) +
88 ' nextgroup=pythonFunction skipwhite')),
89 ("Function","pythonFunction", "match",
90 contained('"[a-zA-Z_][a-zA-Z0-9_]*"')),
91 ("Repeat", "pythonRepeat", "keyword", looping),
92 ("Conditional", "pythonConditional", "keyword",
93 conditionals),
94 ("Operator", "pythonOperator", "keyword", boolean_ops),
95 ("PreCondit", "pythonPreCondit", "keyword", import_stmts),
96 ("Comment", "pythonComment", "match",
97 '"#.*$" contains=pythonTodo'),
98 ("Todo", "pythonTodo", "keyword",
99 contained(' '.join(todos))),
100 ("String", "pythonString", "region", str_regexes()),
101 ("Special", "pythonEscape", "match",
102 (contained(esc) for esc in escapes
103 if not '$' in esc)),
104 ("Special", "pythonEscape", "match", r'"\\$"'),
105 ]
106 ),
107 ("python_highlight_numbers",
108 [("Number", "pythonNumber", "match", numbers)]
109 ),
110 ("python_highlight_builtins",
111 [("Function", "pythonBuiltin", "keyword", builtin_names)]
112 ),
113 ("python_highlight_exceptions",
114 [("Exception", "pythonException", "keyword",
115 exception_names)]
116 ),
117 ("python_highlight_space_errors",
118 [("Error", "pythonSpaceError", "match",
119 ("display " + err for err in space_errors))]
120 )
121 )
122
123def syn_prefix(type_, kind):
124 return 'syn %s %s ' % (type_, kind)
125
126def fill_stmt(iterable, fill_len):
127 """Yield a string that fills at most fill_len characters with strings
128 returned by 'iterable' and separated by a space"""
129 # Deal with trailing char to handle ' '.join() calculation
Tim Petersd6e7e732006-02-26 04:21:50 +0000130 fill_len += 1
Brett Cannona4fe1822006-02-25 14:52:53 +0000131 overflow = None
132 it = iter(iterable)
133 while True:
134 buffer_ = []
135 total_len = 0
136 if overflow:
137 buffer_.append(overflow)
138 total_len += len(overflow) + 1
139 overflow = None
140 while total_len < fill_len:
141 try:
142 new_item = it.next()
143 buffer_.append(new_item)
144 total_len += len(new_item) + 1
145 except StopIteration:
146 if buffer_:
147 break
Brett Cannon20e192b2006-03-01 20:53:08 +0000148 if overflow:
149 yield overflow
150 return
Brett Cannona4fe1822006-02-25 14:52:53 +0000151 if total_len > fill_len:
152 overflow = buffer_.pop()
153 total_len -= len(overflow) - 1
154 ret = ' '.join(buffer_)
155 assert len(ret) <= fill_len
156 yield ret
157
158FILL = 80
159
160def main(file_path):
Brett Cannon20e192b2006-03-01 20:53:08 +0000161 with open(file_path, 'w') as FILE:
Brett Cannona4fe1822006-02-25 14:52:53 +0000162 # Comment for file
163 print>>FILE, comment_header
164 print>>FILE, ''
165 # Statements at start of file
166 print>>FILE, statement_header
167 print>>FILE, ''
168 # Generate case for python_highlight_all
169 print>>FILE, 'if exists("python_highlight_all")'
170 for statement_var, statement_parts in statements:
171 if statement_var:
172 print>>FILE, ' let %s = 1' % statement_var
173 else:
174 print>>FILE, 'endif'
175 print>>FILE, ''
176 # Generate Python groups
177 for statement_var, statement_parts in statements:
178 if statement_var:
179 print>>FILE, 'if exists("%s")' % statement_var
180 indent = ' '
181 else:
182 indent = ''
183 for colour_group, group, type_, arguments in statement_parts:
184 if not isinstance(arguments, basestring):
185 prefix = syn_prefix(type_, group)
186 if type_ == 'keyword':
187 stmt_iter = fill_stmt(arguments,
188 FILL - len(prefix) - len(indent))
189 try:
190 while True:
191 print>>FILE, indent + prefix + stmt_iter.next()
192 except StopIteration:
193 print>>FILE, ''
194 else:
195 for argument in arguments:
196 print>>FILE, indent + prefix + argument
197 else:
198 print>>FILE, ''
199
200 else:
201 print>>FILE, indent + syn_prefix(type_, group) + arguments
202 print>>FILE, ''
203 else:
204 if statement_var:
205 print>>FILE, 'endif'
206 print>>FILE, ''
207 print>>FILE, ''
208 # Associating Python group with Vim colour group
209 for statement_var, statement_parts in statements:
210 if statement_var:
211 print>>FILE, ' if exists("%s")' % statement_var
212 indent = ' '
213 else:
214 indent = ' '
215 for colour_group, group, type_, arguments in statement_parts:
216 print>>FILE, (indent + "hi def link %s %s" %
217 (group, colour_group))
218 else:
219 if statement_var:
220 print>>FILE, ' endif'
221 print>>FILE, ''
222 # Statements at the end of the file
223 print>>FILE, statement_footer
Brett Cannona4fe1822006-02-25 14:52:53 +0000224
225if __name__ == '__main__':
226 main("python.vim")