[include-fixer] Create a mode in vim integration to show multiple potential headers.

Summary:
Some changes in the patch:

* Add two commandline flags in clang-include-fixer.
* Introduce a IncludeFixerContext for the queried symbol.
* Pull out CreateReplacementsForHeader.

Reviewers: bkramer

Subscribers: klimek, cfe-commits, ioeric

Differential Revision: http://reviews.llvm.org/D20621

llvm-svn: 271258
diff --git a/clang-tools-extra/include-fixer/tool/clang-include-fixer.py b/clang-tools-extra/include-fixer/tool/clang-include-fixer.py
index 23154aa..dfb5fcc 100644
--- a/clang-tools-extra/include-fixer/tool/clang-include-fixer.py
+++ b/clang-tools-extra/include-fixer/tool/clang-include-fixer.py
@@ -18,7 +18,6 @@
 import argparse
 import difflib
 import subprocess
-import sys
 import vim
 
 # set g:clang_include_fixer_path to the path to clang-include-fixer if it is not
@@ -28,6 +27,39 @@
 if vim.eval('exists("g:clang_include_fixer_path")') == "1":
   binary = vim.eval('g:clang_include_fixer_path')
 
+maximum_suggested_headers=3
+if vim.eval('exists("g:clang_include_fixer_maximum_suggested_headers")') == "1":
+  maximum_suggested_headers = max(
+      1,
+      vim.eval('g:clang_include_fixer_maximum_suggested_headers'))
+
+
+def ShowDialog(message, choices, default_choice_index=0):
+  to_eval = "confirm('{0}', '{1}', '{2}')".format(message,
+                                                  choices,
+                                                  default_choice_index)
+  return int(vim.eval(to_eval));
+
+
+def execute(command, text):
+  p = subprocess.Popen(command,
+                       stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+                       stdin=subprocess.PIPE)
+  return p.communicate(input=text)
+
+
+def InsertHeaderToVimBuffer(header, text):
+  command = [binary, "-stdin", "-insert-header="+header,
+             vim.current.buffer.name]
+  stdout, stderr = execute(command, text)
+  if stdout:
+    lines = stdout.splitlines()
+    sequence = difflib.SequenceMatcher(None, vim.current.buffer, lines)
+    for op in reversed(sequence.get_opcodes()):
+      if op[0] is not 'equal':
+        vim.current.buffer[op[1]:op[2]] = lines[op[3]:op[4]]
+
+
 def main():
   parser = argparse.ArgumentParser(
       description='Vim integration for clang-include-fixer')
@@ -41,24 +73,36 @@
   buf = vim.current.buffer
   text = '\n'.join(buf)
 
-  # Call clang-include-fixer.
-  command = [binary, "-stdin", "-db="+args.db, "-input="+args.input,
+  # Run command to get all headers.
+  command = [binary, "-stdin", "-output-headers", "-db="+args.db, "-input="+args.input, "-debug",
              vim.current.buffer.name]
-  p = subprocess.Popen(command,
-                       stdout=subprocess.PIPE, stderr=subprocess.PIPE,
-                       stdin=subprocess.PIPE)
-  stdout, stderr = p.communicate(input=text)
+  stdout, stderr = execute(command, text)
+  lines = stdout.splitlines()
+  if len(lines) < 2:
+    print "No header is included.\n"
+    return
 
-  # If successful, replace buffer contents.
-  if stderr:
-    print stderr
+  # The first line is the symbol name.
+  symbol = lines[0]
+  # If there is only one suggested header, insert it directly.
+  if len(lines) == 2 or maximum_suggested_headers == 1:
+    InsertHeaderToVimBuffer(lines[1], text)
+    print "Added #include {0} for {1}.\n".format(lines[1], symbol)
+    return
 
-  if stdout:
-    lines = stdout.splitlines()
-    sequence = difflib.SequenceMatcher(None, vim.current.buffer, lines)
-    for op in reversed(sequence.get_opcodes()):
-      if op[0] is not 'equal':
-        vim.current.buffer[op[1]:op[2]] = lines[op[3]:op[4]]
+  choices_message = ""
+  index = 1;
+  for header in lines[1:1+maximum_suggested_headers]:
+    choices_message += "&" + str(index) + header + "\n"
+    index += 1
+
+  select = ShowDialog("choose a header file for {0}.".format(symbol),
+                      choices_message)
+  # Insert a selected header.
+  InsertHeaderToVimBuffer(lines[select], text)
+  print "Added #include {0} for {1}.\n".format(lines[select], symbol)
+  return;
+
 
 if __name__ == '__main__':
   main()