int LineNumber, const std::vector<Context> &Contexts,
unsigned NumOccurrences = 0);
+ void SetFilePath(llvm::StringRef Path) { FilePath = Path; }
+
/// \brief Get symbol name.
llvm::StringRef getName() const { return Name; }
cl::desc("String to initialize the database"),
cl::cat(IncludeFixerCategory));
+cl::opt<std::string>
+ QuerySymbol("query-symbol",
+ cl::desc("Query a given symbol (e.g. \"a::b::foo\") in\n"
+ "database directly without parsing the file."),
+ cl::cat(IncludeFixerCategory));
+
cl::opt<bool>
MinimizeIncludePaths("minimize-paths",
cl::desc("Whether to minimize added include paths"),
tooling::ClangTool tool(options.getCompilations(),
options.getSourcePathList());
+ llvm::StringRef SourceFilePath = options.getSourcePathList().front();
// In STDINMode, we override the file content with the <stdin> input.
// Since `tool.mapVirtualFile` takes `StringRef`, we define `Code` outside of
// the if-block so that `Code` is not released after the if-block.
if (Code->getBufferSize() == 0)
return 0; // Skip empty files.
- tool.mapVirtualFile(options.getSourcePathList().front(), Code->getBuffer());
+ tool.mapVirtualFile(SourceFilePath, Code->getBuffer());
}
if (!InsertHeader.empty()) {
// Set up data source.
std::unique_ptr<include_fixer::SymbolIndexManager> SymbolIndexMgr =
- createSymbolIndexManager(options.getSourcePathList().front());
+ createSymbolIndexManager(SourceFilePath);
if (!SymbolIndexMgr)
return 1;
+ // Query symbol mode.
+ if (!QuerySymbol.empty()) {
+ auto MatchedSymbols = SymbolIndexMgr->search(QuerySymbol);
+ for (auto &Symbol : MatchedSymbols) {
+ std::string HeaderPath = Symbol.getFilePath().str();
+ Symbol.SetFilePath(((HeaderPath[0] == '"' || HeaderPath[0] == '<')
+ ? HeaderPath
+ : "\"" + HeaderPath + "\""));
+ }
+
+ // We leave an empty symbol range as we don't know the range of the symbol
+ // being queried in this mode. include-fixer won't add namespace qualifiers
+ // if the symbol range is empty, which also fits this case.
+ IncludeFixerContext::QuerySymbolInfo Symbol;
+ Symbol.RawIdentifier = QuerySymbol;
+ auto Context =
+ IncludeFixerContext(SourceFilePath, {Symbol}, MatchedSymbols);
+ writeToJson(llvm::outs(), Context);
+ return 0;
+ }
+
// Now run our tool.
std::vector<include_fixer::IncludeFixerContext> Contexts;
include_fixer::IncludeFixerActionFactory Factory(*SymbolIndexMgr, Contexts,
(defcustom clang-include-fixer-init-string
""
- "clang-include-fixer input format."
+ "clang-include-fixer init string."
:group 'clang-include-fixer
:type 'string
:risky t)
+(defcustom clang-include-fixer-query-mode
+ nil
+ "clang-include-fixer query mode."
+ :group 'clang-include-fixer
+ :type 'boolean
+ :risky t)
(defun clang-include-fixer-call-executable (callee
include-fixer-parameter-a
(message (concat "Calling the include fixer. "
"This might take some seconds. Please wait."))
- (clang-include-fixer-call-executable
- 'clang-include-fixer-add-header
- (concat "-db=" clang-include-fixer-input-format)
- (concat "-input=" clang-include-fixer-init-string)
- "-output-headers"))
+ (if clang-include-fixer-query-mode
+ (let (p1 p2)
+ (save-excursion
+ (skip-chars-backward "-a-zA-Z0-9_:")
+ (setq p1 (point))
+ (skip-chars-forward "-a-zA-Z0-9_:")
+ (setq p2 (point))
+ (setq query-symbol (buffer-substring-no-properties p1 p2))
+ (if (string= "" query-symbol)
+ (message "Skip querying empty symbol.")
+ (clang-include-fixer-call-executable
+ 'clang-include-fixer-add-header
+ (concat "-db=" clang-include-fixer-input-format)
+ (concat "-input=" clang-include-fixer-init-string)
+ (concat "-query-symbol=" (thing-at-point 'symbol))
+ ))))
+ (clang-include-fixer-call-executable
+ 'clang-include-fixer-add-header
+ (concat "-db=" clang-include-fixer-input-format)
+ (concat "-input=" clang-include-fixer-init-string)
+ "-output-headers"))
+ )
(provide 'clang-include-fixer)
;;; clang-include-fixer.el ends here
import argparse
import difflib
+import json
+import re
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.
if vim.eval('exists("g:clang_include_fixer_jump_to_include")') == "1":
jump_to_include = vim.eval('g:clang_include_fixer_jump_to_include') != "0"
+query_mode = True
+if vim.eval('exists("g:clang_include_fixer_query_mode")') == "1":
+ query_mode = vim.eval('g:clang_include_fixer_query_mode') != "0"
+
def GetUserSelection(message, headers, maximum_suggested_headers):
eval_message = message + '\n'
vim.current.window.cursor = (line_num, 0)
+# The vim internal implementation (expand("cword"/"cWORD")) doesn't support
+# our use case very well, we re-implement our own one.
+def get_symbol_under_cursor():
+ line = vim.eval("line(\".\")")
+ # column number in vim is 1-based.
+ col = int(vim.eval("col(\".\")")) - 1
+ line_text = vim.eval("getline({0})".format(line))
+ if len(line_text) == 0: return ""
+ symbol_pos_begin = col
+ p = re.compile('[a-zA-Z0-9:_]')
+ while symbol_pos_begin >= 0 and p.match(line_text[symbol_pos_begin]):
+ symbol_pos_begin -= 1
+
+ symbol_pos_end = col
+ while symbol_pos_end < len(line_text) and p.match(line_text[symbol_pos_end]):
+ symbol_pos_end += 1
+ return line_text[symbol_pos_begin+1:symbol_pos_end]
+
+
def main():
parser = argparse.ArgumentParser(
description='Vim integration for clang-include-fixer')
buf = vim.current.buffer
text = '\n'.join(buf)
- # Run command to get all headers.
- command = [binary, "-stdin", "-output-headers", "-db=" + args.db,
- "-input=" + args.input, vim.current.buffer.name]
+ if query_mode:
+ symbol = get_symbol_under_cursor()
+ if len(symbol) == 0:
+ print "Skip querying empty symbol."
+ return
+ command = [binary, "-stdin", "-query-symbol="+get_symbol_under_cursor(),
+ "-db=" + args.db, "-input=" + args.input,
+ vim.current.buffer.name]
+ else:
+ # Run command to get all headers.
+ command = [binary, "-stdin", "-output-headers", "-db=" + args.db,
+ "-input=" + args.input, vim.current.buffer.name]
stdout, stderr = execute(command, text)
if stderr:
print >> sys.stderr, "Error while running clang-include-fixer: " + stderr
--- /dev/null
+// RUN: clang-include-fixer -db=fixed -input='foo= "foo.h","bar.h"' -query-symbol="foo" test.cpp -- | FileCheck %s
+
+// CHECK: "FilePath": "test.cpp",
+// CHECK-NEXT:"QuerySymbolInfos": [
+// CHECK-NEXT: {"RawIdentifier": "foo",
+// CHECK-NEXT: "Range":{"Offset":0,"Length":0}}
+// CHECK-NEXT:],
+// CHECK-NEXT:"HeaderInfos": [
+// CHECK-NEXT: {"Header": "\"foo.h\"",
+// CHECK-NEXT: "QualifiedName": "foo"},
+// CHECK-NEXT: {"Header": "\"bar.h\"",
+// CHECK-NEXT: "QualifiedName": "foo"}
+// CHECK-NEXT:]