blob: e9714758a7d315c55342f241540fa53ef9fc540d [file] [log] [blame]
Eric Liuc7f3b102016-05-18 14:10:16 +00001# This file is a minimal clang-include-fixer vim-integration. To install:
2# - Change 'binary' if clang-include-fixer is not on the path (see below).
3# - Add to your .vimrc:
4#
5# map ,cf :pyf path/to/llvm/source/tools/clang/tools/extra/include-fixer/tool/clang-include-fixer.py<cr>
6#
7# This enables clang-include-fixer for NORMAL and VISUAL mode. Change ",cf" to
8# another binding if you need clang-include-fixer on a different key.
9#
10# To set up clang-include-fixer, see http://clang.llvm.org/extra/include-fixer.html
11#
12# With this integration you can press the bound key and clang-include-fixer will
13# be run on the current buffer.
14#
15# It operates on the current, potentially unsaved buffer and does not create
16# or save any files. To revert a fix, just undo.
17
18import argparse
Eric Liu702cfd12016-05-19 08:21:09 +000019import difflib
Eric Liuc7f3b102016-05-18 14:10:16 +000020import subprocess
Eric Liuc7f3b102016-05-18 14:10:16 +000021import vim
Haojian Wu17a54e32016-06-01 11:43:10 +000022import json
Eric Liuc7f3b102016-05-18 14:10:16 +000023
24# set g:clang_include_fixer_path to the path to clang-include-fixer if it is not
25# on the path.
26# Change this to the full path if clang-include-fixer is not on the path.
27binary = 'clang-include-fixer'
28if vim.eval('exists("g:clang_include_fixer_path")') == "1":
29 binary = vim.eval('g:clang_include_fixer_path')
30
Haojian Wu11e9bd22016-05-31 09:31:51 +000031maximum_suggested_headers=3
32if vim.eval('exists("g:clang_include_fixer_maximum_suggested_headers")') == "1":
33 maximum_suggested_headers = max(
34 1,
35 vim.eval('g:clang_include_fixer_maximum_suggested_headers'))
36
Eric Liuf4a57102016-06-10 12:09:33 +000037increment_num=5
38if vim.eval('exists("g:clang_include_fixer_increment_num")') == "1":
39 increment_num = max(
40 1,
41 vim.eval('g:clang_include_fixer_increment_num'))
Haojian Wu11e9bd22016-05-31 09:31:51 +000042
Eric Liuf832eb72016-06-07 12:21:43 +000043def GetUserSelection(message, headers, maximum_suggested_headers):
44 eval_message = message + '\n'
45 for idx, header in enumerate(headers[0:maximum_suggested_headers]):
46 eval_message += "({0}). {1}\n".format(idx+1, header)
47 eval_message += "Enter (q) to quit;"
48 if maximum_suggested_headers < len(headers):
Eric Liuf4a57102016-06-10 12:09:33 +000049 eval_message += " (m) to show {0} more candidates.".format(
50 min(increment_num, len(headers) - maximum_suggested_headers))
51
Eric Liuf832eb72016-06-07 12:21:43 +000052 eval_message += "\nSelect (default 1): "
53 res = vim.eval("input('{0}')".format(eval_message))
54 if res == '':
55 # choose the top ranked header by default
56 idx = 1
57 elif res == 'q':
58 raise Exception(' Insertion cancelled...')
Eric Liuf4a57102016-06-10 12:09:33 +000059 elif res == 'm':
60 return GetUserSelection(message,
61 headers, maximum_suggested_headers + increment_num)
Eric Liuf832eb72016-06-07 12:21:43 +000062 else:
63 try:
64 idx = int(res)
65 if idx <= 0 or idx > len(headers):
66 raise Exception()
67 except Exception:
Eric Liuf4a57102016-06-10 12:09:33 +000068 # Show a new prompt on invalid option instead of aborting so that users
69 # don't need to wait for another include-fixer run.
70 print >> sys.stderr, "Invalid option:", res
71 return GetUserSelection(message, headers, maximum_suggested_headers)
Eric Liuf832eb72016-06-07 12:21:43 +000072 return headers[idx-1]
Haojian Wu11e9bd22016-05-31 09:31:51 +000073
74def execute(command, text):
75 p = subprocess.Popen(command,
76 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
77 stdin=subprocess.PIPE)
78 return p.communicate(input=text)
79
80
81def InsertHeaderToVimBuffer(header, text):
Haojian Wu17a54e32016-06-01 11:43:10 +000082 command = [binary, "-stdin", "-insert-header="+json.dumps(header),
Haojian Wu11e9bd22016-05-31 09:31:51 +000083 vim.current.buffer.name]
84 stdout, stderr = execute(command, text)
85 if stdout:
86 lines = stdout.splitlines()
87 sequence = difflib.SequenceMatcher(None, vim.current.buffer, lines)
88 for op in reversed(sequence.get_opcodes()):
89 if op[0] is not 'equal':
90 vim.current.buffer[op[1]:op[2]] = lines[op[3]:op[4]]
91
92
Eric Liuc7f3b102016-05-18 14:10:16 +000093def main():
94 parser = argparse.ArgumentParser(
95 description='Vim integration for clang-include-fixer')
96 parser.add_argument('-db', default='yaml',
97 help='clang-include-fixer input format.')
98 parser.add_argument('-input', default='',
99 help='String to initialize the database.')
100 args = parser.parse_args()
101
102 # Get the current text.
103 buf = vim.current.buffer
104 text = '\n'.join(buf)
105
Haojian Wu11e9bd22016-05-31 09:31:51 +0000106 # Run command to get all headers.
Benjamin Kramer8f961aa2016-05-31 11:28:34 +0000107 command = [binary, "-stdin", "-output-headers", "-db="+args.db,
108 "-input="+args.input, vim.current.buffer.name]
Haojian Wu11e9bd22016-05-31 09:31:51 +0000109 stdout, stderr = execute(command, text)
Haojian Wu17a54e32016-06-01 11:43:10 +0000110 if stderr:
111 print >> sys.stderr, "Error while running clang-include-fixer: " + stderr
112 return
113
114 include_fixer_context = json.loads(stdout)
115 symbol = include_fixer_context["SymbolIdentifier"]
116 headers = include_fixer_context["Headers"]
117
118 if not symbol:
119 print "The file is fine, no need to add a header.\n"
Eric Liuf832eb72016-06-07 12:21:43 +0000120 return
Haojian Wu17a54e32016-06-01 11:43:10 +0000121
122 if not headers:
123 print "Couldn't find a header for {0}.\n".format(symbol)
Haojian Wu11e9bd22016-05-31 09:31:51 +0000124 return
Eric Liuc7f3b102016-05-18 14:10:16 +0000125
Haojian Wu11e9bd22016-05-31 09:31:51 +0000126 # The first line is the symbol name.
Haojian Wu11e9bd22016-05-31 09:31:51 +0000127 # If there is only one suggested header, insert it directly.
Haojian Wu17a54e32016-06-01 11:43:10 +0000128 if len(headers) == 1 or maximum_suggested_headers == 1:
129 InsertHeaderToVimBuffer({"SymbolIdentifier": symbol,
130 "Headers":[headers[0]]}, text)
131 print "Added #include {0} for {1}.\n".format(headers[0], symbol)
Haojian Wu11e9bd22016-05-31 09:31:51 +0000132 return
Eric Liuc7f3b102016-05-18 14:10:16 +0000133
Eric Liuf832eb72016-06-07 12:21:43 +0000134 try:
135 selected = GetUserSelection("choose a header file for {0}.".format(symbol),
136 headers, maximum_suggested_headers)
137 # Insert a selected header.
138 InsertHeaderToVimBuffer({"SymbolIdentifier": symbol,
139 "Headers":[selected]}, text)
140 print "Added #include {0} for {1}.\n".format(selected, symbol)
141 except Exception as error:
142 print >> sys.stderr, error.message
143 return
Haojian Wu11e9bd22016-05-31 09:31:51 +0000144
Eric Liuc7f3b102016-05-18 14:10:16 +0000145
146if __name__ == '__main__':
147 main()