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