| 1 | from __future__ import with_statement
|
|---|
| 2 |
|
|---|
| 3 | import keyword
|
|---|
| 4 | import exceptions
|
|---|
| 5 | import __builtin__
|
|---|
| 6 | from string import Template
|
|---|
| 7 |
|
|---|
| 8 | comment_header = """" Auto-generated Vim syntax file for Python
|
|---|
| 9 | "
|
|---|
| 10 | " To use: copy or symlink to ~/.vim/syntax/python.vim"""
|
|---|
| 11 |
|
|---|
| 12 | statement_header = """
|
|---|
| 13 | if exists("b:current_syntax")
|
|---|
| 14 | finish
|
|---|
| 15 | endif"""
|
|---|
| 16 |
|
|---|
| 17 | statement_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
|
|---|
| 21 | syn sync match pythonSync grouphere NONE "):$"
|
|---|
| 22 | syn sync maxlines=200
|
|---|
| 23 | "syn sync minlines=2000
|
|---|
| 24 |
|
|---|
| 25 | let b:current_syntax = "python"'''
|
|---|
| 26 |
|
|---|
| 27 | looping = ('for', 'while')
|
|---|
| 28 | conditionals = ('if', 'elif', 'else')
|
|---|
| 29 | boolean_ops = ('and', 'in', 'is', 'not', 'or')
|
|---|
| 30 | import_stmts = ('import', 'from')
|
|---|
| 31 | object_defs = ('def', 'class')
|
|---|
| 32 |
|
|---|
| 33 | exception_names = frozenset(exc for exc in dir(exceptions)
|
|---|
| 34 | 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
|
|---|
| 40 | builtin_names = frozenset(builtin for builtin in dir(__builtin__)
|
|---|
| 41 | if builtin not in dir(exceptions))
|
|---|
| 42 |
|
|---|
| 43 | escapes = (r'+\\[abfnrtv\'"\\]+', r'"\\\o\{1,3}"', r'"\\x\x\{2}"',
|
|---|
| 44 | r'"\(\\u\x\{4}\|\\U\x\{8}\)"', r'"\\$"')
|
|---|
| 45 |
|
|---|
| 46 | todos = ("TODO", "FIXME", "XXX")
|
|---|
| 47 |
|
|---|
| 48 | # XXX codify?
|
|---|
| 49 | numbers = (r'"\<0x\x\+[Ll]\=\>"', r'"\<\d\+[LljJ]\=\>"',
|
|---|
| 50 | '"\.\d\+\([eE][+-]\=\d\+\)\=[jJ]\=\>"',
|
|---|
| 51 | '"\<\d\+\.\([eE][+-]\=\d\+\)\=[jJ]\=\>"',
|
|---|
| 52 | '"\<\d\+\.\d\+\([eE][+-]\=\d\+\)\=[jJ]\=\>"')
|
|---|
| 53 |
|
|---|
| 54 | contained = lambda x: "%s contained" % x
|
|---|
| 55 |
|
|---|
| 56 | def 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 = ''
|
|---|
| 70 | contains = 'contains=pythonEscape' if not raw else ''
|
|---|
| 71 | yield regex_template.substitute(raw=raw, sep=separator, skip=skip,
|
|---|
| 72 | contains = contains)
|
|---|
| 73 |
|
|---|
| 74 | space_errors = (r'excludenl "\S\s\+$"ms=s+1', r'" \+\t"', r'"\t\+ "')
|
|---|
| 75 |
|
|---|
| 76 | statements = (
|
|---|
| 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 |
|
|---|
| 123 | def syn_prefix(type_, kind):
|
|---|
| 124 | return 'syn %s %s ' % (type_, kind)
|
|---|
| 125 |
|
|---|
| 126 | def 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
|
|---|
| 130 | fill_len += 1
|
|---|
| 131 | 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
|
|---|
| 148 | if overflow:
|
|---|
| 149 | yield overflow
|
|---|
| 150 | return
|
|---|
| 151 | 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 |
|
|---|
| 158 | FILL = 80
|
|---|
| 159 |
|
|---|
| 160 | def main(file_path):
|
|---|
| 161 | with open(file_path, 'w') as FILE:
|
|---|
| 162 | # 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
|
|---|
| 224 |
|
|---|
| 225 | if __name__ == '__main__':
|
|---|
| 226 | main("python.vim")
|
|---|