Index: include-fixer/IncludeFixer.cpp =================================================================== --- include-fixer/IncludeFixer.cpp +++ include-fixer/IncludeFixer.cpp @@ -193,10 +193,7 @@ getIncludeFixerContext(const clang::SourceManager &SourceManager, clang::HeaderSearch &HeaderSearch) { IncludeFixerContext FixerContext; - if (SymbolQueryResults.empty()) - return FixerContext; - - FixerContext.SymbolIdentifer = QuerySymbol; + FixerContext.SymbolIdentifier = QuerySymbol; for (const auto &Header : SymbolQueryResults) FixerContext.Headers.push_back( minimizeInclude(Header, SourceManager, HeaderSearch)); Index: include-fixer/IncludeFixerContext.h =================================================================== --- include-fixer/IncludeFixerContext.h +++ include-fixer/IncludeFixerContext.h @@ -19,7 +19,7 @@ /// \brief A context for the symbol being queried. struct IncludeFixerContext { /// \brief The symbol name. - std::string SymbolIdentifer; + std::string SymbolIdentifier; /// \brief The headers which have SymbolIdentifier definitions. std::vector Headers; }; Index: include-fixer/tool/ClangIncludeFixer.cpp =================================================================== --- include-fixer/tool/ClangIncludeFixer.cpp +++ include-fixer/tool/ClangIncludeFixer.cpp @@ -18,9 +18,25 @@ #include "clang/Tooling/CommonOptionsParser.h" #include "clang/Tooling/Tooling.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/YAMLTraits.h" using namespace clang; using namespace llvm; +using clang::include_fixer::IncludeFixerContext; + +LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(IncludeFixerContext) +LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(std::string) + +namespace llvm { +namespace yaml { +template <> struct MappingTraits { + static void mapping(IO &io, IncludeFixerContext &Context) { + io.mapRequired("SymbolIdentifier", Context.SymbolIdentifier); + io.mapRequired("Headers", Context.Headers); + } +}; +} // namespace yaml +} // namespace llvm namespace { cl::OptionCategory IncludeFixerCategory("Tool options"); @@ -60,19 +76,21 @@ cl::opt OutputHeaders( "output-headers", - cl::desc("Output the symbol being quired and all its relevant headers.\n" - "The first line is the symbol name; The other lines\n" - "are the headers: \n" - " b::foo\n" - " path/to/foo_a.h\n" - " path/to/foo_b.h\n"), + cl::desc("Print the symbol being queried and all its relevant headers in\n" + "JSON format to stdout:\n" + " {\n" + " \"SymbolIdentifier\": \"foo\",\n" + " \"Headers\": [\"\\\"foo_a.h\\\"\"]\n" + " }"), cl::init(false), cl::cat(IncludeFixerCategory)); cl::opt InsertHeader( "insert-header", cl::desc("Insert a specific header. This should run with STDIN mode.\n" "The result is written to stdout. It is currently used for\n" - "editor integration."), + "editor integration. Support YAML/JSON format:\n" + " -insert-header=\"{SymbolIdentifier: foo,\n" + " Headers: ['\\\"foo_a.h\\\"']}\""), cl::init(""), cl::cat(IncludeFixerCategory)); cl::opt @@ -134,6 +152,24 @@ return SymbolIndexMgr; } +void writeToJson(llvm::raw_ostream &OS, const IncludeFixerContext& Context) { + OS << "{\n" + " \"SymbolIdentifier\": \"" << Context.SymbolIdentifier << "\",\n" + " \"Headers\": [ "; + for (const auto &Header : Context.Headers) { + // Escape double quote. + if (Header.front() == '"') + OS << " \"" << llvm::yaml::escape(Header) << "\""; + else + OS << " \"" << Header << "\""; + + if (Header != Context.Headers.back()) + OS << ", "; + } + OS << " ]\n" + "}\n"; +} + int includeFixerMain(int argc, const char **argv) { tooling::CommonOptionsParser options(argc, argv, IncludeFixerCategory); tooling::ClangTool tool(options.getCompilations(), @@ -168,9 +204,18 @@ return 1; } + llvm::yaml::Input yin(InsertHeader); + IncludeFixerContext Context; + yin >> Context; + + if (Context.Headers.size() != 1) { + errs() << "Expect exactly one inserted header.\n"; + return 1; + } + tooling::Replacements Replacements = clang::include_fixer::createInsertHeaderReplacements( - Code->getBuffer(), FilePath, InsertHeader, InsertStyle); + Code->getBuffer(), FilePath, Context.Headers[0], InsertStyle); tooling::Replacements Replaces(Replacements.begin(), Replacements.end()); std::string ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), Replaces); @@ -196,10 +241,7 @@ } if (OutputHeaders) { - // FIXME: Output IncludeFixerContext as YAML. - llvm::outs() << Context.SymbolIdentifer << "\n"; - for (const auto &Header : Context.Headers) - llvm::outs() << Header << "\n"; + writeToJson(llvm::outs(), Context); return 0; } Index: include-fixer/tool/clang-include-fixer.py =================================================================== --- include-fixer/tool/clang-include-fixer.py +++ include-fixer/tool/clang-include-fixer.py @@ -19,6 +19,7 @@ import difflib import subprocess import vim +import json # set g:clang_include_fixer_path to the path to clang-include-fixer if it is not # on the path. @@ -49,7 +50,7 @@ def InsertHeaderToVimBuffer(header, text): - command = [binary, "-stdin", "-insert-header="+header, + command = [binary, "-stdin", "-insert-header="+json.dumps(header), vim.current.buffer.name] stdout, stderr = execute(command, text) if stdout: @@ -77,30 +78,42 @@ command = [binary, "-stdin", "-output-headers", "-db="+args.db, "-input="+args.input, vim.current.buffer.name] stdout, stderr = execute(command, text) - lines = stdout.splitlines() - if len(lines) < 2: - print "No header is included.\n" + if stderr: + print >> sys.stderr, "Error while running clang-include-fixer: " + stderr + return + + include_fixer_context = json.loads(stdout) + symbol = include_fixer_context["SymbolIdentifier"] + headers = include_fixer_context["Headers"] + + if not symbol: + print "The file is fine, no need to add a header.\n" + return; + + if not headers: + print "Couldn't find a header for {0}.\n".format(symbol) return # 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) + if len(headers) == 1 or maximum_suggested_headers == 1: + InsertHeaderToVimBuffer({"SymbolIdentifier": symbol, + "Headers":[headers[0]]}, text) + print "Added #include {0} for {1}.\n".format(headers[0], symbol) return choices_message = "" index = 1; - for header in lines[1:1+maximum_suggested_headers]: + for header in headers[0:maximum_suggested_headers]: choices_message += "&{0} {1}\n".format(index, header) 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) + InsertHeaderToVimBuffer({"SymbolIdentifier": symbol, + "Headers":[headers[select-1]]}, text) + print "Added #include {0} for {1}.\n".format(headers[select-1], symbol) return; Index: test/include-fixer/commandline_options.cpp =================================================================== --- test/include-fixer/commandline_options.cpp +++ test/include-fixer/commandline_options.cpp @@ -1,10 +1,9 @@ // REQUIRES: shell // RUN: sed -e 's#//.*$##' %s > %t.cpp // RUN: clang-include-fixer -db=fixed -input='foo= "foo.h","bar.h"' -output-headers %t.cpp -- | FileCheck %s -check-prefix=CHECK-HEADERS -// RUN: cat %t.cpp | clang-include-fixer -stdin -insert-header='"foo.h"' %t.cpp | FileCheck %s -check-prefix=CHECK +// RUN: cat %t.cpp | clang-include-fixer -stdin -insert-header='{SymbolIdentifier: foo, Headers: ["\"foo.h\""]}' %t.cpp | FileCheck %s -check-prefix=CHECK // -// CHECK-HEADERS: "foo.h" -// CHECK-HEADERS: "bar.h" +// CHECK-HEADERS: "Headers": [ "\"foo.h\"", "\"bar.h\"" ] // // CHECK: #include "foo.h" // CHECK: foo f; Index: test/include-fixer/ranking.cpp =================================================================== --- test/include-fixer/ranking.cpp +++ test/include-fixer/ranking.cpp @@ -1,6 +1,5 @@ -// RUN: clang-include-fixer -db=yaml -input=%S/Inputs/fake_yaml_db.yaml -output-headers %s -- | FileCheck %s -implicit-check-not=.h +// RUN: clang-include-fixer -db=yaml -input=%S/Inputs/fake_yaml_db.yaml -output-headers %s -- | FileCheck %s -// CHECK: "../include/bar.h" -// CHECK-NEXT: "../include/zbar.h" +// CHECK: "Headers": [ "\"../include/bar.h\"", "\"../include/zbar.h\"" ] bar b;