forked from qt-creator/qt-creator
Clang: Add refactoring support for unsaved content
We need the generated UI header but we don't have a build directory. So we provide clang with in memory represations of the file. Change-Id: Ie9db97bbea2222b0203a0457baa1f1fc7ad97213 Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
@@ -31,7 +31,8 @@ namespace {
|
||||
|
||||
// use std::filesystem::path if it is supported by all compilers
|
||||
|
||||
std::string toNativePath(std::string &&path)
|
||||
template <typename String>
|
||||
String toNativePath(String &&path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
std::replace(path.begin(), path.end(), '/', '\\');
|
||||
@@ -75,15 +76,52 @@ void ClangTool::addFiles(const Utils::SmallStringVector &filePaths,
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
Utils::SmallString toNativeFilePath(const FilePath &filePath)
|
||||
{
|
||||
Utils::SmallString filePathString = filePath.directory().clone();
|
||||
filePathString.append("/");
|
||||
filePathString.append(filePath.name());
|
||||
|
||||
return toNativePath(std::move(filePathString));
|
||||
}
|
||||
}
|
||||
|
||||
void ClangTool::addUnsavedFiles(std::vector<V2::FileContainer> &&unsavedFiles)
|
||||
{
|
||||
unsavedFileContents.reserve(unsavedFileContents.size() + unsavedFiles.size());
|
||||
|
||||
auto convertToUnsavedFileContent = [] (V2::FileContainer &unsavedFile) {
|
||||
return UnsavedFileContent{toNativeFilePath(unsavedFile.filePath()),
|
||||
unsavedFile.takeUnsavedFileContent()};
|
||||
};
|
||||
|
||||
std::transform(unsavedFiles.begin(),
|
||||
unsavedFiles.end(),
|
||||
std::back_inserter(unsavedFileContents),
|
||||
convertToUnsavedFileContent);
|
||||
}
|
||||
|
||||
namespace {
|
||||
llvm::StringRef toStringRef(const Utils::SmallString &string)
|
||||
{
|
||||
return llvm::StringRef(string.data(), string.size());
|
||||
}
|
||||
}
|
||||
|
||||
clang::tooling::ClangTool ClangTool::createTool() const
|
||||
{
|
||||
clang::tooling::ClangTool tool(compilationDatabase, sourceFilePaths);
|
||||
|
||||
for (auto &&fileContent : fileContents) {
|
||||
for (const auto &fileContent : fileContents) {
|
||||
if (!fileContent.content.empty())
|
||||
tool.mapVirtualFile(fileContent.filePath, fileContent.content);
|
||||
}
|
||||
|
||||
for (const auto &unsavedFileContent : unsavedFileContents)
|
||||
tool.mapVirtualFile(toStringRef(unsavedFileContent.filePath),
|
||||
toStringRef(unsavedFileContent.content));
|
||||
|
||||
return tool;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include <clangrefactoringbackend_global.h>
|
||||
|
||||
#include <filecontainerv2.h>
|
||||
#include <sourcelocationscontainer.h>
|
||||
|
||||
#if defined(__GNUC__)
|
||||
@@ -42,6 +43,8 @@
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#include <utils/smallstring.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -67,6 +70,18 @@ struct FileContent
|
||||
std::vector<std::string> commandLine;
|
||||
};
|
||||
|
||||
struct UnsavedFileContent
|
||||
{
|
||||
UnsavedFileContent(Utils::SmallString &&filePath,
|
||||
Utils::SmallString &&content)
|
||||
: filePath(std::move(filePath)),
|
||||
content(std::move(content))
|
||||
{}
|
||||
|
||||
Utils::SmallString filePath;
|
||||
Utils::SmallString content;
|
||||
};
|
||||
|
||||
class ClangTool
|
||||
{
|
||||
public:
|
||||
@@ -77,12 +92,15 @@ public:
|
||||
void addFiles(const Utils::SmallStringVector &filePaths,
|
||||
const Utils::SmallStringVector &arguments);
|
||||
|
||||
void addUnsavedFiles(std::vector<V2::FileContainer> &&unsavedFiles);
|
||||
|
||||
clang::tooling::ClangTool createTool() const;
|
||||
|
||||
private:
|
||||
RefactoringCompilationDatabase compilationDatabase;
|
||||
std::vector<FileContent> fileContents;
|
||||
std::vector<std::string> sourceFilePaths;
|
||||
std::vector<UnsavedFileContent> unsavedFileContents;
|
||||
};
|
||||
|
||||
} // namespace ClangBackEnd
|
||||
|
||||
@@ -72,7 +72,9 @@ void RefactoringServer::requestSourceLocationsForRenamingMessage(RequestSourceLo
|
||||
void RefactoringServer::requestSourceRangesAndDiagnosticsForQueryMessage(
|
||||
RequestSourceRangesAndDiagnosticsForQueryMessage &&message)
|
||||
{
|
||||
gatherSourceRangesAndDiagnosticsForQueryMessage(message.takeFileContainers(), message.takeQuery());
|
||||
gatherSourceRangesAndDiagnosticsForQueryMessage(message.takeSources(),
|
||||
message.takeUnsavedContent(),
|
||||
message.takeQuery());
|
||||
}
|
||||
|
||||
void RefactoringServer::cancel()
|
||||
@@ -93,16 +95,19 @@ void RefactoringServer::supersedePollEventLoop(std::function<void ()> &&pollEven
|
||||
namespace {
|
||||
|
||||
SourceRangesAndDiagnosticsForQueryMessage createSourceRangesAndDiagnosticsForQueryMessage(
|
||||
V2::FileContainer &&fileContainer,
|
||||
V2::FileContainer &&source,
|
||||
std::vector<V2::FileContainer> &&unsaved,
|
||||
Utils::SmallString &&query,
|
||||
const std::atomic_bool &cancelWork) {
|
||||
ClangQuery clangQuery(std::move(query));
|
||||
|
||||
if (!cancelWork) {
|
||||
clangQuery.addFile(fileContainer.filePath().directory(),
|
||||
fileContainer.filePath().name(),
|
||||
fileContainer.takeUnsavedFileContent(),
|
||||
fileContainer.takeCommandLineArguments());
|
||||
clangQuery.addFile(source.filePath().directory(),
|
||||
source.filePath().name(),
|
||||
source.takeUnsavedFileContent(),
|
||||
source.takeCommandLineArguments());
|
||||
|
||||
clangQuery.addUnsavedFiles(std::move(unsaved));
|
||||
|
||||
clangQuery.findLocations();
|
||||
}
|
||||
@@ -114,27 +119,30 @@ SourceRangesAndDiagnosticsForQueryMessage createSourceRangesAndDiagnosticsForQue
|
||||
}
|
||||
|
||||
void RefactoringServer::gatherSourceRangesAndDiagnosticsForQueryMessage(
|
||||
std::vector<V2::FileContainer> &&fileContainers,
|
||||
std::vector<V2::FileContainer> &&sources,
|
||||
std::vector<V2::FileContainer> &&unsaved,
|
||||
Utils::SmallString &&query)
|
||||
{
|
||||
std::vector<Future> futures;
|
||||
|
||||
std::size_t freeProcessors = std::thread::hardware_concurrency();
|
||||
|
||||
while (!fileContainers.empty() || !futures.empty()) {
|
||||
while (!sources.empty() || !futures.empty()) {
|
||||
--freeProcessors;
|
||||
|
||||
if (!fileContainers.empty()) {
|
||||
if (!sources.empty()) {
|
||||
Future &&future = std::async(std::launch::async,
|
||||
createSourceRangesAndDiagnosticsForQueryMessage,
|
||||
std::move(fileContainers.back()),
|
||||
query.clone(), std::ref(cancelWork));
|
||||
fileContainers.pop_back();
|
||||
std::move(sources.back()),
|
||||
Utils::clone(unsaved),
|
||||
query.clone(),
|
||||
std::ref(cancelWork));
|
||||
sources.pop_back();
|
||||
|
||||
futures.emplace_back(std::move(future));
|
||||
}
|
||||
|
||||
if (freeProcessors == 0 || fileContainers.empty())
|
||||
if (freeProcessors == 0 || sources.empty())
|
||||
freeProcessors += waitForNewSourceRangesAndDiagnosticsForQueryMessage(futures);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,8 @@ public:
|
||||
void supersedePollEventLoop(std::function<void()> &&pollEventLoop);
|
||||
|
||||
private:
|
||||
void gatherSourceRangesAndDiagnosticsForQueryMessage(std::vector<V2::FileContainer> &&fileContainers,
|
||||
void gatherSourceRangesAndDiagnosticsForQueryMessage(std::vector<V2::FileContainer> &&sources,
|
||||
std::vector<V2::FileContainer> &&unsaved,
|
||||
Utils::SmallString &&query);
|
||||
std::size_t waitForNewSourceRangesAndDiagnosticsForQueryMessage(std::vector<Future> &futures);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user