Skip to content

Commit

Permalink
[include-fixer] Support finding headers for the symbol under cursor.
Browse files Browse the repository at this point in the history
Summary:
* Add a `query-symbol` option to query symbol without parsing the source file.
* Update Vim & Emacs integration scripts.

Reviewers: bkramer, massberg

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D24075

llvm-svn: 280824
  • Loading branch information
hokein committed Sep 7, 2016
1 parent f2a0289 commit cd63701
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 12 deletions.
2 changes: 2 additions & 0 deletions clang-tools-extra/include-fixer/find-all-symbols/SymbolInfo.h
Expand Up @@ -54,6 +54,8 @@ class SymbolInfo {
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; }

Expand Down
32 changes: 30 additions & 2 deletions clang-tools-extra/include-fixer/tool/ClangIncludeFixer.cpp
Expand Up @@ -98,6 +98,12 @@ cl::opt<std::string> Input("input",
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"),
Expand Down Expand Up @@ -236,6 +242,7 @@ int includeFixerMain(int argc, const char **argv) {
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.
Expand All @@ -253,7 +260,7 @@ int includeFixerMain(int argc, const char **argv) {
if (Code->getBufferSize() == 0)
return 0; // Skip empty files.

tool.mapVirtualFile(options.getSourcePathList().front(), Code->getBuffer());
tool.mapVirtualFile(SourceFilePath, Code->getBuffer());
}

if (!InsertHeader.empty()) {
Expand Down Expand Up @@ -314,10 +321,31 @@ int includeFixerMain(int argc, const char **argv) {

// 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,
Expand Down
35 changes: 29 additions & 6 deletions clang-tools-extra/include-fixer/tool/clang-include-fixer.el
Expand Up @@ -36,11 +36,17 @@

(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
Expand Down Expand Up @@ -197,11 +203,28 @@
(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
41 changes: 37 additions & 4 deletions clang-tools-extra/include-fixer/tool/clang-include-fixer.py
Expand Up @@ -17,9 +17,10 @@

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.
Expand All @@ -44,6 +45,10 @@
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'
Expand Down Expand Up @@ -105,6 +110,25 @@ def InsertHeaderToVimBuffer(header, text):
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')
Expand All @@ -118,9 +142,18 @@ def main():
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
Expand Down
13 changes: 13 additions & 0 deletions clang-tools-extra/test/include-fixer/query_symbol.cpp
@@ -0,0 +1,13 @@
// 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:]

0 comments on commit cd63701

Please sign in to comment.