summaryrefslogtreecommitdiffstats
path: root/Misc/Vim/vim_syntax.py
diff options
context:
space:
mode:
authorBrett Cannon <bcannon@gmail.com>2006-02-25 14:52:53 (GMT)
committerBrett Cannon <bcannon@gmail.com>2006-02-25 14:52:53 (GMT)
commita4fe18227d48edbb41b76f2861be2c6b800c6609 (patch)
tree666bc9322232ff4f78cfe24359b6dbf140a6de30 /Misc/Vim/vim_syntax.py
parentd074beb6925a87874600b605a91a23263b3a6028 (diff)
downloadcpython-a4fe18227d48edbb41b76f2861be2c6b800c6609.zip
cpython-a4fe18227d48edbb41b76f2861be2c6b800c6609.tar.gz
cpython-a4fe18227d48edbb41b76f2861be2c6b800c6609.tar.bz2
Add a script that auto-generates a Vim syntax highlighting file for Python.
Just symlink or copy python.vim to ~/.vim/syntax/ . Also included is a sample Python file with basic expressions to make sure they are highlighted. Also add a Vim directory in Misc to hold all Vim configuration files.
Diffstat (limited to 'Misc/Vim/vim_syntax.py')
-rw-r--r--Misc/Vim/vim_syntax.py232
1 files changed, 232 insertions, 0 deletions
diff --git a/Misc/Vim/vim_syntax.py b/Misc/Vim/vim_syntax.py
new file mode 100644
index 0000000..b733929
--- /dev/null
+++ b/Misc/Vim/vim_syntax.py
@@ -0,0 +1,232 @@
+import keyword
+import exceptions
+import __builtin__
+from string import Template
+
+comment_header = """" Auto-generated Vim syntax file for Python
+"
+" To use: copy or symlink to ~/.vim/syntax/python.vim"""
+
+statement_header = """
+if exists("b:current_syntax")
+ finish
+endif"""
+
+statement_footer = '''
+" Uncomment the 'minlines' statement line and comment out the 'maxlines'
+" statement line; changes behaviour to look at least 2000 lines previously for
+" syntax matches instead of at most 200 lines
+syn sync match pythonSync grouphere NONE "):$"
+syn sync maxlines=200
+"syn sync minlines=2000
+
+let b:current_syntax = "python"'''
+
+looping = ('for', 'while')
+conditionals = ('if', 'elif', 'else')
+boolean_ops = ('and', 'in', 'is', 'not', 'or')
+import_stmts = ('import', 'from')
+object_defs = ('def', 'class')
+
+exception_names = frozenset(exc for exc in dir(exceptions)
+ if not exc.startswith('__'))
+
+# Need to include functions that start with '__' (e.g., __import__), but
+# nothing that comes with modules (e.g., __name__), so just exclude anything in
+# the 'exceptions' module since we want to ignore exceptions *and* what any
+# module would have
+builtin_names = frozenset(builtin for builtin in dir(__builtin__)
+ if builtin not in dir(exceptions))
+
+escapes = (r'+\\[abfnrtv\'"\\]+', r'"\\\o\{1,3}"', r'"\\x\x\{2}"',
+ r'"\(\\u\x\{4}\|\\U\x\{8}\)"', r'"\\$"')
+
+todos = ("TODO", "FIXME", "XXX")
+
+# XXX codify?
+numbers = (r'"\<0x\x\+[Ll]\=\>"', r'"\<\d\+[LljJ]\=\>"',
+ '"\.\d\+\([eE][+-]\=\d\+\)\=[jJ]\=\>"',
+ '"\<\d\+\.\([eE][+-]\=\d\+\)\=[jJ]\=\>"',
+ '"\<\d\+\.\d\+\([eE][+-]\=\d\+\)\=[jJ]\=\>"')
+
+contained = lambda x: "%s contained" % x
+
+def str_regexes():
+ """Generator to yield various combinations of strings regexes"""
+ regex_template = Template('matchgroup=Normal ' +
+ 'start=+[uU]\=${raw}${sep}+ ' +
+ 'end=+${sep}+ ' +
+ '${skip} ' +
+ '${contains}')
+ skip_regex = Template(r'skip=+\\\\\|\\${sep}+')
+ for raw in ('', '[rR]'):
+ for separator in ("'", '"', '"""', "'''"):
+ if len(separator) == 1:
+ skip = skip_regex.substitute(sep=separator)
+ else:
+ skip = ''
+ if not raw:
+ contains = 'contains=pythonEscape'
+ else:
+ contains = ''
+ yield regex_template.substitute(raw=raw, sep=separator, skip=skip,
+ contains = contains)
+
+space_errors = (r'excludenl "\S\s\+$"ms=s+1', r'" \+\t"', r'"\t\+ "')
+
+statements = (
+ ('',
+ # XXX Might need to change pythonStatement since have
+ # specific Repeat, Conditional, Operator, etc. for 'while',
+ # etc.
+ [("Statement", "pythonStatement", "keyword",
+ (kw for kw in keyword.kwlist
+ if kw not in (looping + conditionals + boolean_ops +
+ import_stmts + object_defs))
+ ),
+ ("Statement", "pythonStatement", "keyword",
+ (' '.join(object_defs) +
+ ' nextgroup=pythonFunction skipwhite')),
+ ("Function","pythonFunction", "match",
+ contained('"[a-zA-Z_][a-zA-Z0-9_]*"')),
+ ("Repeat", "pythonRepeat", "keyword", looping),
+ ("Conditional", "pythonConditional", "keyword",
+ conditionals),
+ ("Operator", "pythonOperator", "keyword", boolean_ops),
+ ("PreCondit", "pythonPreCondit", "keyword", import_stmts),
+ ("Comment", "pythonComment", "match",
+ '"#.*$" contains=pythonTodo'),
+ ("Todo", "pythonTodo", "keyword",
+ contained(' '.join(todos))),
+ ("String", "pythonString", "region", str_regexes()),
+ ("Special", "pythonEscape", "match",
+ (contained(esc) for esc in escapes
+ if not '$' in esc)),
+ ("Special", "pythonEscape", "match", r'"\\$"'),
+ ]
+ ),
+ ("python_highlight_numbers",
+ [("Number", "pythonNumber", "match", numbers)]
+ ),
+ ("python_highlight_builtins",
+ [("Function", "pythonBuiltin", "keyword", builtin_names)]
+ ),
+ ("python_highlight_exceptions",
+ [("Exception", "pythonException", "keyword",
+ exception_names)]
+ ),
+ ("python_highlight_space_errors",
+ [("Error", "pythonSpaceError", "match",
+ ("display " + err for err in space_errors))]
+ )
+ )
+
+def syn_prefix(type_, kind):
+ return 'syn %s %s ' % (type_, kind)
+
+def fill_stmt(iterable, fill_len):
+ """Yield a string that fills at most fill_len characters with strings
+ returned by 'iterable' and separated by a space"""
+ # Deal with trailing char to handle ' '.join() calculation
+ fill_len += 1
+ overflow = None
+ it = iter(iterable)
+ while True:
+ buffer_ = []
+ total_len = 0
+ if overflow:
+ buffer_.append(overflow)
+ total_len += len(overflow) + 1
+ overflow = None
+ while total_len < fill_len:
+ try:
+ new_item = it.next()
+ buffer_.append(new_item)
+ total_len += len(new_item) + 1
+ except StopIteration:
+ if buffer_:
+ break
+ if not buffer_ and overflow:
+ yield buffer_
+ return
+ else:
+ return
+ if total_len > fill_len:
+ overflow = buffer_.pop()
+ total_len -= len(overflow) - 1
+ ret = ' '.join(buffer_)
+ assert len(ret) <= fill_len
+ yield ret
+
+FILL = 80
+
+def main(file_path):
+ FILE = open(file_path, 'w')
+ try:
+ # Comment for file
+ print>>FILE, comment_header
+ print>>FILE, ''
+ # Statements at start of file
+ print>>FILE, statement_header
+ print>>FILE, ''
+ # Generate case for python_highlight_all
+ print>>FILE, 'if exists("python_highlight_all")'
+ for statement_var, statement_parts in statements:
+ if statement_var:
+ print>>FILE, ' let %s = 1' % statement_var
+ else:
+ print>>FILE, 'endif'
+ print>>FILE, ''
+ # Generate Python groups
+ for statement_var, statement_parts in statements:
+ if statement_var:
+ print>>FILE, 'if exists("%s")' % statement_var
+ indent = ' '
+ else:
+ indent = ''
+ for colour_group, group, type_, arguments in statement_parts:
+ if not isinstance(arguments, basestring):
+ prefix = syn_prefix(type_, group)
+ if type_ == 'keyword':
+ stmt_iter = fill_stmt(arguments,
+ FILL - len(prefix) - len(indent))
+ try:
+ while True:
+ print>>FILE, indent + prefix + stmt_iter.next()
+ except StopIteration:
+ print>>FILE, ''
+ else:
+ for argument in arguments:
+ print>>FILE, indent + prefix + argument
+ else:
+ print>>FILE, ''
+
+ else:
+ print>>FILE, indent + syn_prefix(type_, group) + arguments
+ print>>FILE, ''
+ else:
+ if statement_var:
+ print>>FILE, 'endif'
+ print>>FILE, ''
+ print>>FILE, ''
+ # Associating Python group with Vim colour group
+ for statement_var, statement_parts in statements:
+ if statement_var:
+ print>>FILE, ' if exists("%s")' % statement_var
+ indent = ' '
+ else:
+ indent = ' '
+ for colour_group, group, type_, arguments in statement_parts:
+ print>>FILE, (indent + "hi def link %s %s" %
+ (group, colour_group))
+ else:
+ if statement_var:
+ print>>FILE, ' endif'
+ print>>FILE, ''
+ # Statements at the end of the file
+ print>>FILE, statement_footer
+ finally:
+ FILE.close()
+
+if __name__ == '__main__':
+ main("python.vim")