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