Cling patch for code completion.
This commit is contained in:
parent
8ef4931a12
commit
cc5da51f21
@ -62,6 +62,12 @@ namespace cling {
|
||||
///
|
||||
unsigned CheckPointerValidity : 1;
|
||||
|
||||
///\brief Offset into the input line to enable the setting of the
|
||||
/// code completion point.
|
||||
/// -1 diasables code completion.
|
||||
///
|
||||
int CodeCompletionOffset = -1;
|
||||
|
||||
CompilationOptions() {
|
||||
DeclarationExtraction = 0;
|
||||
ValuePrinting = VPDisabled;
|
||||
@ -84,7 +90,8 @@ namespace cling {
|
||||
CodeGeneration == Other.CodeGeneration &&
|
||||
CodeGenerationForModule == Other.CodeGenerationForModule &&
|
||||
IgnorePromptDiags == Other.IgnorePromptDiags &&
|
||||
CheckPointerValidity == Other.CheckPointerValidity;
|
||||
CheckPointerValidity == Other.CheckPointerValidity &&
|
||||
CodeCompletionOffset == Other.CodeCompletionOffset;
|
||||
}
|
||||
|
||||
bool operator!=(CompilationOptions Other) const {
|
||||
@ -97,7 +104,8 @@ namespace cling {
|
||||
CodeGeneration != Other.CodeGeneration ||
|
||||
CodeGenerationForModule != Other.CodeGenerationForModule ||
|
||||
IgnorePromptDiags != Other.IgnorePromptDiags ||
|
||||
CheckPointerValidity != Other.CheckPointerValidity;
|
||||
CheckPointerValidity != Other.CheckPointerValidity ||
|
||||
CodeCompletionOffset != Other.CodeCompletionOffset;
|
||||
}
|
||||
};
|
||||
} // end namespace cling
|
||||
|
@ -303,7 +303,7 @@ namespace cling {
|
||||
///\param[in] llvmdir - ???
|
||||
///\param[in] noRuntime - flag to control the presence of runtime universe
|
||||
///
|
||||
Interpreter(Interpreter &parentInterpreter,int argc, const char* const *argv,
|
||||
Interpreter(const Interpreter &parentInterpreter,int argc, const char* const *argv,
|
||||
const char* llvmdir = 0, bool noRuntime = true);
|
||||
|
||||
virtual ~Interpreter();
|
||||
@ -467,6 +467,18 @@ namespace cling {
|
||||
///
|
||||
CompilationResult parseForModule(const std::string& input);
|
||||
|
||||
///\brief Code completes user input.
|
||||
///
|
||||
/// The interface circumvents the most of the extra work necessary to
|
||||
/// code complete code.
|
||||
///
|
||||
/// @param[in] input - The input containing the string to be completed.
|
||||
/// @param[out] T - The cling::Transaction of the input
|
||||
///
|
||||
///\returns Whether the operation was fully successful.
|
||||
///
|
||||
CompilationResult codeComplete(const std::string& input, unsigned offset);
|
||||
|
||||
///\brief Compiles input line, which doesn't contain statements.
|
||||
///
|
||||
/// The interface circumvents the most of the extra work necessary to
|
||||
|
@ -30,6 +30,7 @@ namespace clang {
|
||||
class Token;
|
||||
class FileEntry;
|
||||
class Module;
|
||||
class CodeCompleteConsumer;
|
||||
}
|
||||
|
||||
namespace cling {
|
||||
@ -161,6 +162,13 @@ namespace cling {
|
||||
/// for instance when throwing interpreter exceptions.
|
||||
virtual void PrintStackTrace() {}
|
||||
|
||||
///\brief Cling creates the code complete consumer for its child interp.
|
||||
virtual void CreateCodeCompleteConsumer(Interpreter* child) const;
|
||||
|
||||
///\brief Get the resukts of the code completion.
|
||||
virtual void GetCompletionResults(Interpreter* child,
|
||||
std::vector<std::string>&) const;
|
||||
|
||||
///\brief DynamicScopes only! Set to true if it is currently evaluating a
|
||||
/// dynamic expr.
|
||||
///
|
||||
|
@ -109,6 +109,8 @@ namespace cling {
|
||||
|
||||
const Interpreter& getInterpreter() const { return m_Interp; }
|
||||
|
||||
Interpreter& getInterpreter() { return m_Interp; }
|
||||
|
||||
///\brief Get the output stream used by the MetaProcessor for its output.
|
||||
/// (in contrast to the interpreter's output which is redirected using
|
||||
/// setStdStream()).
|
||||
|
@ -3,9 +3,37 @@
|
||||
|
||||
using namespace clang;
|
||||
|
||||
namespace {
|
||||
class ClingASTImporter : public ASTImporter {
|
||||
private:
|
||||
cling::ASTImportSource& m_source;
|
||||
|
||||
public:
|
||||
ClingASTImporter(ASTContext &ToContext, FileManager &ToFileManager,
|
||||
ASTContext &FromContext, FileManager &FromFileManager,
|
||||
bool MinimalImport, cling::ASTImportSource& source) :
|
||||
ASTImporter(ToContext, ToFileManager, FromContext,
|
||||
FromFileManager, MinimalImport), m_source(source) {}
|
||||
|
||||
Decl *Imported(Decl *From, Decl *To) override {
|
||||
ASTImporter::Imported(From, To);
|
||||
|
||||
// Put the name of the Decl imported with the
|
||||
// DeclarationName coming from the parent, in my map.
|
||||
if (DeclContext *declContextParent = llvm::dyn_cast<DeclContext>(From)) {
|
||||
DeclContext *declContextChild = llvm::dyn_cast<DeclContext>(To);
|
||||
m_source.addToDeclContext(declContextChild, declContextParent);
|
||||
}
|
||||
|
||||
return To;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
namespace cling {
|
||||
|
||||
ASTImportSource::ASTImportSource(cling::Interpreter *parent_interpreter,
|
||||
ASTImportSource::ASTImportSource(const cling::Interpreter *parent_interpreter,
|
||||
cling::Interpreter *child_interpreter) :
|
||||
m_parent_Interp(parent_interpreter), m_child_Interp(child_interpreter) {
|
||||
|
||||
@ -88,10 +116,10 @@ namespace cling {
|
||||
FileManager &child_FM = m_child_Interp->getCI()->getFileManager();
|
||||
FileManager &parent_FM = m_parent_Interp->getCI()->getFileManager();
|
||||
|
||||
// Clang's ASTImporter
|
||||
ASTImporter importer(to_ASTContext, child_FM,
|
||||
// Cling's ASTImporter
|
||||
ClingASTImporter importer(to_ASTContext, child_FM,
|
||||
from_ASTContext, parent_FM,
|
||||
/*MinimalImport : ON*/ true);
|
||||
/*MinimalImport : ON*/ true, *this);
|
||||
|
||||
for (DeclContext::lookup_iterator I = lookup_result.begin(),
|
||||
E = lookup_result.end();
|
||||
@ -120,6 +148,8 @@ namespace cling {
|
||||
bool ASTImportSource::FindExternalVisibleDeclsByName(
|
||||
const DeclContext *childCurrentDeclContext, DeclarationName childDeclName) {
|
||||
|
||||
assert(childDeclName && "Child Decl name is empty");
|
||||
|
||||
assert(childCurrentDeclContext->hasExternalVisibleStorage() &&
|
||||
"DeclContext has no visible decls in storage");
|
||||
|
||||
@ -132,21 +162,24 @@ namespace cling {
|
||||
} else {
|
||||
// Get the identifier info from the parent interpreter
|
||||
// for this Name.
|
||||
llvm::StringRef name(childDeclName.getAsString());
|
||||
IdentifierTable &parentIdentifierTable =
|
||||
m_parent_Interp->getCI()->getASTContext().Idents;
|
||||
IdentifierInfo &parentIdentifierInfo = parentIdentifierTable.get(name);
|
||||
DeclarationName parentDeclNameTemp(&parentIdentifierInfo);
|
||||
parentDeclName = parentDeclNameTemp;
|
||||
std::string name = childDeclName.getAsString();
|
||||
if (!name.empty()) {
|
||||
llvm::StringRef nameRef(name);
|
||||
IdentifierTable &parentIdentifierTable =
|
||||
m_parent_Interp->getCI()->getASTContext().Idents;
|
||||
IdentifierInfo &parentIdentifierInfo =
|
||||
parentIdentifierTable.get(nameRef);
|
||||
parentDeclName = DeclarationName(&parentIdentifierInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// Search in the map of the stored Decl Contexts for this
|
||||
// Decl Context.
|
||||
std::map<const clang::DeclContext *, clang::DeclContext *>::iterator I;
|
||||
// If childCurrentDeclContext was found before and is already in the map,
|
||||
// then do the lookup using the stored pointer.
|
||||
if ((I = m_DeclContexts_map.find(childCurrentDeclContext))
|
||||
!= m_DeclContexts_map.end()) {
|
||||
// If childCurrentDeclContext was found before and is already in the map,
|
||||
// then do the lookup using the stored pointer.
|
||||
DeclContext *parentDeclContext = I->second;
|
||||
|
||||
Decl *fromDeclContext = Decl::castFromDeclContext(parentDeclContext);
|
||||
@ -168,4 +201,55 @@ namespace cling {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
///\brief Make available to child all decls in parent's decl context
|
||||
/// that corresponds to child decl context.
|
||||
void ASTImportSource::completeVisibleDeclsMap(
|
||||
const clang::DeclContext *childDeclContext) {
|
||||
assert (childDeclContext && "No child decl context!");
|
||||
|
||||
if (!childDeclContext->hasExternalVisibleStorage())
|
||||
return;
|
||||
|
||||
// Search in the map of the stored Decl Contexts for this
|
||||
// Decl Context.
|
||||
std::map<const clang::DeclContext *, clang::DeclContext *>::iterator I;
|
||||
// If childCurrentDeclContext was found before and is already in the map,
|
||||
// then do the lookup using the stored pointer.
|
||||
if ((I = m_DeclContexts_map.find(childDeclContext))
|
||||
!= m_DeclContexts_map.end()) {
|
||||
|
||||
DeclContext *parentDeclContext = I->second;
|
||||
|
||||
Decl *fromDeclContext = Decl::castFromDeclContext(parentDeclContext);
|
||||
ASTContext &from_ASTContext = fromDeclContext->getASTContext();
|
||||
|
||||
Decl *toDeclContext = Decl::castFromDeclContext(childDeclContext);
|
||||
ASTContext &to_ASTContext = toDeclContext->getASTContext();
|
||||
|
||||
// Prepare to import the Decl(Context) we found in the
|
||||
// child interpreter by getting the file managers from
|
||||
// each interpreter.
|
||||
FileManager &child_FM = m_child_Interp->getCI()->getFileManager();
|
||||
FileManager &parent_FM = m_parent_Interp->getCI()->getFileManager();
|
||||
|
||||
// Cling's ASTImporter
|
||||
ClingASTImporter importer(to_ASTContext, child_FM,
|
||||
from_ASTContext, parent_FM,
|
||||
/*MinimalImport : ON*/ true, *this);
|
||||
|
||||
for (DeclContext::decl_iterator I_decl = parentDeclContext->decls_begin(),
|
||||
E_decl = parentDeclContext->decls_end(); I_decl != E_decl; ++I_decl) {
|
||||
if (NamedDecl* parentNamedDecl = llvm::dyn_cast<NamedDecl>(*I_decl)) {
|
||||
DeclarationName newDeclName = parentNamedDecl->getDeclName();
|
||||
ImportDecl(parentNamedDecl, importer, newDeclName, newDeclName,
|
||||
childDeclContext);
|
||||
}
|
||||
}
|
||||
|
||||
const_cast<DeclContext *>(childDeclContext)->
|
||||
setHasExternalVisibleStorage(false);
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace cling
|
||||
|
@ -35,7 +35,7 @@ namespace cling {
|
||||
class ASTImportSource : public clang::ExternalASTSource {
|
||||
|
||||
private:
|
||||
cling::Interpreter *m_parent_Interp;
|
||||
const cling::Interpreter *m_parent_Interp;
|
||||
cling::Interpreter *m_child_Interp;
|
||||
|
||||
clang::Sema *m_Sema;
|
||||
@ -56,11 +56,13 @@ namespace cling {
|
||||
std::map <clang::DeclarationName, clang::DeclarationName > m_DeclName_map;
|
||||
|
||||
public:
|
||||
ASTImportSource(cling::Interpreter *parent_interpreter,
|
||||
ASTImportSource(const cling::Interpreter *parent_interpreter,
|
||||
cling::Interpreter *child_interpreter);
|
||||
|
||||
~ASTImportSource() { };
|
||||
|
||||
void completeVisibleDeclsMap(const clang::DeclContext *DC) override;
|
||||
|
||||
bool FindExternalVisibleDeclsByName(const clang::DeclContext *childCurrentDeclContext,
|
||||
clang::DeclarationName childDeclName) override;
|
||||
|
||||
@ -86,6 +88,11 @@ namespace cling {
|
||||
clang::DeclarationName &childDeclName,
|
||||
clang::DeclarationName &parentDeclName,
|
||||
const clang::DeclContext *childCurrentDeclContext);
|
||||
|
||||
void addToDeclContext(clang::DeclContext *child,
|
||||
clang::DeclContext *parent) {
|
||||
m_DeclContexts_map[child] = parent;
|
||||
}
|
||||
};
|
||||
} // end namespace cling
|
||||
#endif //CLING_ASTIMPORTSOURCE_H
|
||||
|
@ -26,6 +26,7 @@ add_cling_library(clingInterpreter
|
||||
CheckEmptyTransactionTransformer.cpp
|
||||
CIFactory.cpp
|
||||
ClangInternalState.cpp
|
||||
ClingCodeCompleteConsumer.cpp
|
||||
ClingPragmas.cpp
|
||||
DeclCollector.cpp
|
||||
DeclExtractor.cpp
|
||||
|
@ -713,10 +713,20 @@ namespace cling {
|
||||
SourceLocation NewLoc = getLastMemoryBufferEndLoc().getLocWithOffset(1);
|
||||
|
||||
llvm::MemoryBuffer* MBNonOwn = MB.get();
|
||||
// Create FileID for the current buffer
|
||||
FileID FID = SM.createFileID(std::move(MB), SrcMgr::C_User,
|
||||
/*LoadedID*/0,
|
||||
/*LoadedOffset*/0, NewLoc);
|
||||
|
||||
// Create FileEntry and FileID for the current buffer
|
||||
const clang::FileEntry* FE
|
||||
= SM.getFileManager().getVirtualFile("vfile for " + source_name.str(),
|
||||
InputSize, 0 /* mod time*/);
|
||||
SM.overrideFileContents(FE, std::move(MB));
|
||||
FileID FID = SM.createFileID(FE, NewLoc, SrcMgr::C_User);
|
||||
|
||||
// Set the code completion point if completion is enabled.
|
||||
if (CO.CodeCompletionOffset != -1) {
|
||||
printf("Is it really the completion point?\n");
|
||||
PP.SetCodeCompletionPoint(FE, 1, 45 + CO.CodeCompletionOffset);
|
||||
m_Interpreter->unload(1);
|
||||
}
|
||||
|
||||
m_MemoryBuffers.push_back(std::make_pair(MBNonOwn, FID));
|
||||
|
||||
@ -767,6 +777,11 @@ namespace cling {
|
||||
m_Consumer->HandleTopLevelDecl(ADecl.get());
|
||||
};
|
||||
|
||||
if (CO.CodeCompletionOffset != -1) {
|
||||
assert(PP.isCodeCompletionReached() && "Code completion set but not reached!");
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
#ifdef LLVM_ON_WIN32
|
||||
// Microsoft-specific:
|
||||
// Late parsed templates can leave unswallowed "macro"-like tokens.
|
||||
|
@ -237,7 +237,7 @@ namespace cling {
|
||||
///\brief Constructor for the child Interpreter.
|
||||
/// Passing the parent Interpreter as an argument.
|
||||
///
|
||||
Interpreter::Interpreter(Interpreter &parentInterpreter, int argc,
|
||||
Interpreter::Interpreter(const Interpreter &parentInterpreter, int argc,
|
||||
const char* const *argv,
|
||||
const char* llvmdir /*= 0*/, bool noRuntime) :
|
||||
Interpreter(argc, argv, llvmdir, noRuntime, /* isChildInterp */ true) {
|
||||
@ -255,7 +255,7 @@ namespace cling {
|
||||
|
||||
// Inform the Translation Unit Decl of I2 that it has to search somewhere
|
||||
// else to find the declarations.
|
||||
getCI()->getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage();
|
||||
getCI()->getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage(true);
|
||||
|
||||
// Give my IncrementalExecutor a pointer to the Incremental executor of the
|
||||
// parent Interpreter.
|
||||
@ -639,6 +639,34 @@ namespace cling {
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
||||
Interpreter::CompilationResult
|
||||
Interpreter::codeComplete(const std::string& input, unsigned offset) {
|
||||
|
||||
CompilationOptions CO;
|
||||
CO.DeclarationExtraction = 0;
|
||||
CO.ValuePrinting = 0;
|
||||
CO.ResultEvaluation = 0;
|
||||
CO.DynamicScoping = isDynamicLookupEnabled();
|
||||
CO.Debug = isPrintingDebug();
|
||||
CO.CheckPointerValidity = 0;
|
||||
CO.CodeCompletionOffset = offset;
|
||||
|
||||
|
||||
std::string wrappedInput = input;
|
||||
std::string wrapperName;
|
||||
if (ShouldWrapInput(input))
|
||||
WrapInput(wrappedInput, wrapperName);
|
||||
|
||||
StateDebuggerRAII stateDebugger(this);
|
||||
|
||||
// This triggers the FileEntry to be created and the completion
|
||||
// point to be set in clang.
|
||||
m_IncrParser->Parse(wrappedInput, CO);
|
||||
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
Interpreter::CompilationResult
|
||||
Interpreter::declare(const std::string& input, Transaction** T/*=0 */) {
|
||||
CompilationOptions CO;
|
||||
|
@ -9,9 +9,11 @@
|
||||
|
||||
#include "cling/Interpreter/InterpreterCallbacks.h"
|
||||
|
||||
#include "cling/Interpreter/ClingCodeCompleteConsumer.h"
|
||||
#include "cling/Interpreter/Interpreter.h"
|
||||
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/Sema/CodeCompleteConsumer.h"
|
||||
#include "clang/Frontend/CompilerInstance.h"
|
||||
#include "clang/Lex/Preprocessor.h"
|
||||
#include "clang/Sema/Sema.h"
|
||||
@ -239,6 +241,27 @@ namespace cling {
|
||||
return false;
|
||||
}
|
||||
|
||||
void InterpreterCallbacks::CreateCodeCompleteConsumer(Interpreter* child) const {
|
||||
ClingCodeCompleteConsumer* consumer = new ClingCodeCompleteConsumer(
|
||||
m_Interpreter->getCI()->getFrontendOpts().CodeCompleteOpts,
|
||||
llvm::outs());
|
||||
CompilerInstance* childCI = child->getCI();
|
||||
//codeCompletionCI will own consumer!
|
||||
childCI->setCodeCompletionConsumer(consumer);
|
||||
clang::Sema& childSemaRef = childCI->getSema();
|
||||
childSemaRef.CodeCompleter = consumer;
|
||||
}
|
||||
|
||||
void InterpreterCallbacks::GetCompletionResults(Interpreter* child,
|
||||
std::vector<std::string>& completions) const {
|
||||
// Which cast to use , fno-rtti
|
||||
ClingCodeCompleteConsumer* consumer =
|
||||
(ClingCodeCompleteConsumer*)(
|
||||
child->getCI()->getSema().CodeCompleter);
|
||||
if (consumer)
|
||||
consumer->getCompletions(completions);
|
||||
}
|
||||
|
||||
void InterpreterCallbacks::UpdateWithNewDecls(const DeclContext *DC,
|
||||
DeclarationName Name,
|
||||
llvm::ArrayRef<NamedDecl*> Decls) {
|
||||
|
@ -25,6 +25,8 @@
|
||||
#include "clang/Basic/LangOptions.h"
|
||||
#include "clang/Frontend/CompilerInstance.h"
|
||||
|
||||
#include "ClingTabCompletion.h"
|
||||
|
||||
// Fragment copied from LLVM's raw_ostream.cpp
|
||||
#if defined(HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
@ -118,6 +120,11 @@ namespace cling {
|
||||
std::unique_ptr<TerminalDisplay> D(TerminalDisplay::Create());
|
||||
TextInput TI(*R, *D, histfilePath.empty() ? 0 : histfilePath.c_str());
|
||||
|
||||
// Inform text input about the code complete consumer
|
||||
ClingTabCompletion* CompletionConsumer = new ClingTabCompletion(m_MetaProcessor->getInterpreter());
|
||||
TI.SetCompletion(CompletionConsumer);
|
||||
|
||||
|
||||
TI.SetPrompt("[cling]$ ");
|
||||
std::string line;
|
||||
while (true) {
|
||||
|
Loading…
Reference in New Issue
Block a user