Subversion Repositories QNX 8.QNX8 LLVM/Clang compiler suite

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. # This file is a minimal clang-format vim-integration. To install:
  2. # - Change 'binary' if clang-format is not on the path (see below).
  3. # - Add to your .vimrc:
  4. #
  5. #   if has('python')
  6. #     map <C-I> :pyf <path-to-this-file>/clang-format.py<cr>
  7. #     imap <C-I> <c-o>:pyf <path-to-this-file>/clang-format.py<cr>
  8. #   elseif has('python3')
  9. #     map <C-I> :py3f <path-to-this-file>/clang-format.py<cr>
  10. #     imap <C-I> <c-o>:py3f <path-to-this-file>/clang-format.py<cr>
  11. #   endif
  12. #
  13. # The if-elseif-endif conditional should pick either the python3 or python2
  14. # integration depending on your vim setup.
  15. #
  16. # The first mapping enables clang-format for NORMAL and VISUAL mode, the second
  17. # mapping adds support for INSERT mode. Change "C-I" to another binding if you
  18. # need clang-format on a different key (C-I stands for Ctrl+i).
  19. #
  20. # With this integration you can press the bound key and clang-format will
  21. # format the current line in NORMAL and INSERT mode or the selected region in
  22. # VISUAL mode. The line or region is extended to the next bigger syntactic
  23. # entity.
  24. #
  25. # You can also pass in the variable "l:lines" to choose the range for
  26. # formatting. This variable can either contain "<start line>:<end line>" or
  27. # "all" to format the full file. So, to format the full file, write a function
  28. # like:
  29. # :function FormatFile()
  30. # :  let l:lines="all"
  31. # :  if has('python')
  32. # :    pyf <path-to-this-file>/clang-format.py
  33. # :  elseif has('python3')
  34. # :    py3f <path-to-this-file>/clang-format.py
  35. # :  endif
  36. # :endfunction
  37. #
  38. # It operates on the current, potentially unsaved buffer and does not create
  39. # or save any files. To revert a formatting, just undo.
  40. from __future__ import absolute_import, division, print_function
  41.  
  42. import difflib
  43. import json
  44. import os.path
  45. import platform
  46. import subprocess
  47. import sys
  48. import vim
  49.  
  50. # set g:clang_format_path to the path to clang-format if it is not on the path
  51. # Change this to the full path if clang-format is not on the path.
  52. binary = 'clang-format'
  53. if vim.eval('exists("g:clang_format_path")') == "1":
  54.   binary = vim.eval('g:clang_format_path')
  55.  
  56. # Change this to format according to other formatting styles. See the output of
  57. # 'clang-format --help' for a list of supported styles. The default looks for
  58. # a '.clang-format' or '_clang-format' file to indicate the style that should be
  59. # used.
  60. style = None
  61. fallback_style = None
  62. if vim.eval('exists("g:clang_format_fallback_style")') == "1":
  63.   fallback_style = vim.eval('g:clang_format_fallback_style')
  64.  
  65. def get_buffer(encoding):
  66.   if platform.python_version_tuple()[0] == '3':
  67.     return vim.current.buffer
  68.   return [ line.decode(encoding) for line in vim.current.buffer ]
  69.  
  70. def main():
  71.   # Get the current text.
  72.   encoding = vim.eval("&encoding")
  73.   buf = get_buffer(encoding)
  74.   # Join the buffer into a single string with a terminating newline
  75.   text = ('\n'.join(buf) + '\n').encode(encoding)
  76.  
  77.   # Determine range to format.
  78.   if vim.eval('exists("l:lines")') == '1':
  79.     lines = ['-lines', vim.eval('l:lines')]
  80.   elif vim.eval('exists("l:formatdiff")') == '1' and \
  81.        os.path.exists(vim.current.buffer.name):
  82.     with open(vim.current.buffer.name, 'r') as f:
  83.       ondisk = f.read().splitlines();
  84.     sequence = difflib.SequenceMatcher(None, ondisk, vim.current.buffer)
  85.     lines = []
  86.     for op in reversed(sequence.get_opcodes()):
  87.       if op[0] not in ['equal', 'delete']:
  88.         lines += ['-lines', '%s:%s' % (op[3] + 1, op[4])]
  89.     if lines == []:
  90.       return
  91.   else:
  92.     lines = ['-lines', '%s:%s' % (vim.current.range.start + 1,
  93.                                   vim.current.range.end + 1)]
  94.  
  95.   # Convert cursor (line, col) to bytes.
  96.   # Don't use line2byte: https://github.com/vim/vim/issues/5930
  97.   _, cursor_line, cursor_col, _ = vim.eval('getpos(".")') # 1-based
  98.   cursor_byte = 0
  99.   for line in text.split(b'\n')[:int(cursor_line) - 1]:
  100.     cursor_byte += len(line) + 1
  101.   cursor_byte += int(cursor_col) - 1
  102.   if cursor_byte < 0:
  103.     print('Couldn\'t determine cursor position. Is your file empty?')
  104.     return
  105.  
  106.   # Avoid flashing an ugly, ugly cmd prompt on Windows when invoking clang-format.
  107.   startupinfo = None
  108.   if sys.platform.startswith('win32'):
  109.     startupinfo = subprocess.STARTUPINFO()
  110.     startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
  111.     startupinfo.wShowWindow = subprocess.SW_HIDE
  112.  
  113.   # Call formatter.
  114.   command = [binary, '-cursor', str(cursor_byte)]
  115.   if lines != ['-lines', 'all']:
  116.     command += lines
  117.   if style:
  118.     command.extend(['-style', style])
  119.   if fallback_style:
  120.     command.extend(['-fallback-style', fallback_style])
  121.   if vim.current.buffer.name:
  122.     command.extend(['-assume-filename', vim.current.buffer.name])
  123.   p = subprocess.Popen(command,
  124.                        stdout=subprocess.PIPE, stderr=subprocess.PIPE,
  125.                        stdin=subprocess.PIPE, startupinfo=startupinfo)
  126.   stdout, stderr = p.communicate(input=text)
  127.  
  128.   # If successful, replace buffer contents.
  129.   if stderr:
  130.     print(stderr)
  131.  
  132.   if not stdout:
  133.     print(
  134.         'No output from clang-format (crashed?).\n'
  135.         'Please report to bugs.llvm.org.'
  136.     )
  137.   else:
  138.     header, content = stdout.split(b'\n', 1)
  139.     header = json.loads(header.decode('utf-8'))
  140.     # Strip off the trailing newline (added above).
  141.     # This maintains trailing empty lines present in the buffer if
  142.     # the -lines specification requests them to remain unchanged.
  143.     lines = content.decode(encoding).split('\n')[:-1]
  144.     sequence = difflib.SequenceMatcher(None, buf, lines)
  145.     for op in reversed(sequence.get_opcodes()):
  146.       if op[0] != 'equal':
  147.         vim.current.buffer[op[1]:op[2]] = lines[op[3]:op[4]]
  148.     if header.get('IncompleteFormat'):
  149.       print('clang-format: incomplete (syntax errors)')
  150.     # Convert cursor bytes to (line, col)
  151.     # Don't use goto: https://github.com/vim/vim/issues/5930
  152.     cursor_byte = int(header['Cursor'])
  153.     prefix = content[0:cursor_byte]
  154.     cursor_line = 1 + prefix.count(b'\n')
  155.     cursor_column = 1 + len(prefix.rsplit(b'\n', 1)[-1])
  156.     vim.command('call cursor(%d, %d)' % (cursor_line, cursor_column))
  157.  
  158. main()
  159.