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