diff --git a/include/cling/Interpreter/ClingCodeCompleteConsumer.h b/include/cling/Interpreter/ClingCodeCompleteConsumer.h new file mode 100644 index 00000000..c4c8cf97 --- /dev/null +++ b/include/cling/Interpreter/ClingCodeCompleteConsumer.h @@ -0,0 +1,48 @@ +#ifndef CLINGCODECOMPLETECONSUMER_H +#define CLINGCODECOMPLETECONSUMER_H + +#include "clang/Sema/CodeCompleteConsumer.h" + +using namespace clang; + +class ClingCodeCompleteConsumer : public CodeCompleteConsumer { + /// \brief The raw output stream. + raw_ostream &OS; + + CodeCompletionTUInfo CCTUInfo; + /// \ brief Results of the completer to be printed by the text interface. + std::vector m_completions; + +public: + /// \brief Create a new printing code-completion consumer that prints its + /// results to the given raw output stream. + ClingCodeCompleteConsumer(const CodeCompleteOptions &CodeCompleteOpts, + raw_ostream &OS) + : CodeCompleteConsumer(CodeCompleteOpts, false), OS(OS), + CCTUInfo(new GlobalCodeCompletionAllocator) {} + + ~ClingCodeCompleteConsumer() {} + + /// \brief Prints the finalized code-completion results. + void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context, + CodeCompletionResult *Results, + unsigned NumResults) override; + + void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg, + OverloadCandidate *Candidates, + unsigned NumCandidates) override {} + + bool isResultFilteredOut(StringRef Filter, CodeCompletionResult Results) override; + + CodeCompletionAllocator &getAllocator() override { + return CCTUInfo.getAllocator(); + } + + CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; } + + void getCompletions(std::vector& completions) { + completions = m_completions; + } +}; + +#endif \ No newline at end of file diff --git a/include/cling/Interpreter/ClingTabCompletion.h b/include/cling/Interpreter/ClingTabCompletion.h new file mode 100644 index 00000000..e81f552c --- /dev/null +++ b/include/cling/Interpreter/ClingTabCompletion.h @@ -0,0 +1,23 @@ +#ifndef CLINGTABCOMPLETION_H +#define CLINGTABCOMPLETION_H + +#include +#include + +namespace cling { + + class Interpreter; + + class ClingTabCompletion { + const cling::Interpreter& ParentInterp; + + public: + ClingTabCompletion(cling::Interpreter& Parent) : ParentInterp(Parent) {} + ~ClingTabCompletion() {} + + bool Complete(const std::string& Line /*in+out*/, + size_t& Cursor /*in+out*/, + std::vector& DisplayCompletions /*out*/); + }; +} +#endif diff --git a/include/cling/Interpreter/InterpreterCallbacks.h b/include/cling/Interpreter/InterpreterCallbacks.h index 7f2e4e58..c9923924 100644 --- a/include/cling/Interpreter/InterpreterCallbacks.h +++ b/include/cling/Interpreter/InterpreterCallbacks.h @@ -34,6 +34,7 @@ namespace clang { } namespace cling { + class ClingTabCompletion; class Interpreter; class InterpreterCallbacks; class InterpreterDeserializationListener; @@ -45,6 +46,11 @@ namespace cling { /// interpreter as it does its thing. Clients can define their hooks here to /// implement interpreter level tools. class InterpreterCallbacks { + private: + ///\brief Cling Code Completion object. + /// + ClingTabCompletion* m_Completer; + protected: ///\brief Our interpreter instance. @@ -165,10 +171,14 @@ namespace cling { ///\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. + ///\brief Get the results of the code completion. virtual void GetCompletionResults(Interpreter* child, std::vector&) const; + ///\brief Get the results of the code completion. + virtual void CodeComplete(const std::string&, size_t&, + std::vector&) const; + ///\brief DynamicScopes only! Set to true if it is currently evaluating a /// dynamic expr. /// diff --git a/lib/Interpreter/CMakeLists.txt b/lib/Interpreter/CMakeLists.txt index fcd76913..1eb31d9b 100644 --- a/lib/Interpreter/CMakeLists.txt +++ b/lib/Interpreter/CMakeLists.txt @@ -27,6 +27,7 @@ add_cling_library(clingInterpreter CIFactory.cpp ClangInternalState.cpp ClingCodeCompleteConsumer.cpp + ClingTabCompletion.cpp ClingPragmas.cpp DeclCollector.cpp DeclExtractor.cpp diff --git a/lib/Interpreter/ClingCodeCompleteConsumer.cpp b/lib/Interpreter/ClingCodeCompleteConsumer.cpp new file mode 100644 index 00000000..320320f0 --- /dev/null +++ b/lib/Interpreter/ClingCodeCompleteConsumer.cpp @@ -0,0 +1,74 @@ +#include "cling/Interpreter/ClingCodeCompleteConsumer.h" + +#include "clang/Sema/Sema.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Lex/Preprocessor.h" + + +void +ClingCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &SemaRef, + CodeCompletionContext Context, + CodeCompletionResult *Results, + unsigned NumResults) { + std::stable_sort(Results, Results + NumResults); + + StringRef Filter = SemaRef.getPreprocessor().getCodeCompletionFilter(); + + // Print the results. + for (unsigned I = 0; I != NumResults; ++I) { + if(!Filter.empty() && isResultFilteredOut(Filter, Results[I])) + continue; + switch (Results[I].Kind) { + case CodeCompletionResult::RK_Declaration: + if (CodeCompletionString *CCS + = Results[I].CreateCodeCompletionString(SemaRef, Context, + getAllocator(), + CCTUInfo, + includeBriefComments())) { + m_completions.push_back(CCS->getAsString()); + } + break; + + case CodeCompletionResult::RK_Keyword: + m_completions.push_back(Results[I].Keyword); + break; + + case CodeCompletionResult::RK_Macro: { + if (CodeCompletionString *CCS + = Results[I].CreateCodeCompletionString(SemaRef, Context, + getAllocator(), + CCTUInfo, + includeBriefComments())) { + m_completions.push_back(CCS->getAsString()); + } + break; + } + + case CodeCompletionResult::RK_Pattern: { + m_completions.push_back(Results[I].Pattern->getAsString()); + break; + } + } + } +} + +bool ClingCodeCompleteConsumer::isResultFilteredOut(StringRef Filter, + CodeCompletionResult Result) { + switch (Result.Kind) { + case CodeCompletionResult::RK_Declaration: { + return !(Result.Declaration->getIdentifier() && + (*Result.Declaration).getIdentifier()->getName().startswith(Filter)); + } + case CodeCompletionResult::RK_Keyword: { + return !((StringRef(Result.Keyword)).startswith(Filter)); + } + case CodeCompletionResult::RK_Macro: { + return !(Result.Macro->getName().startswith(Filter)); + } + case CodeCompletionResult::RK_Pattern: { + return !(StringRef((Result.Pattern->getAsString())).startswith(Filter)); + } + default: llvm_unreachable("Unknown code completion result Kind."); + } +} diff --git a/lib/Interpreter/ClingTabCompletion.cpp b/lib/Interpreter/ClingTabCompletion.cpp new file mode 100644 index 00000000..beba0a32 --- /dev/null +++ b/lib/Interpreter/ClingTabCompletion.cpp @@ -0,0 +1,49 @@ + +#include "cling/Interpreter/ClingTabCompletion.h" + +#include "cling/Interpreter/Interpreter.h" +#include "clang/Sema/Sema.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Frontend/CompilerInstance.h" +#include "cling/Interpreter/InterpreterCallbacks.h" + +namespace cling { + + bool ClingTabCompletion::Complete(const std::string& Line /*in+out*/, + size_t& Cursor /*in+out*/, + std::vector& DisplayCompletions /*out*/) { + //Get the results + const char * const argV = "cling"; + cling::Interpreter CodeCompletionInterp(ParentInterp, 1, &argV); + + // Create the CodeCompleteConsumer with InterpreterCallbacks + // from the parent interpreter and set the consumer for the child + // interpreter + // Yuck! But I need the const/non-const to be fixed somehow. + + const InterpreterCallbacks* callbacks = ParentInterp.getCallbacks(); + callbacks->CreateCodeCompleteConsumer(&CodeCompletionInterp); + + clang::CompilerInstance* codeCompletionCI = CodeCompletionInterp.getCI(); + clang::Sema& codeCompletionSemaRef = codeCompletionCI->getSema(); + // Ignore diagnostics when we tab complete + clang::IgnoringDiagConsumer* ignoringDiagConsumer = new clang::IgnoringDiagConsumer(); + codeCompletionSemaRef.getDiagnostics().setClient(ignoringDiagConsumer, true); + + auto Owner = ParentInterp.getCI()->getSema().getDiagnostics().takeClient(); + auto Client = ParentInterp.getCI()->getSema().getDiagnostics().getClient(); + ParentInterp.getCI()->getSema().getDiagnostics().setClient(ignoringDiagConsumer, false); + CodeCompletionInterp.codeComplete(Line, Cursor); + + callbacks->GetCompletionResults(&CodeCompletionInterp, DisplayCompletions); + // Restore the original diag client for parent interpreter + ParentInterp.getCI()->getSema().getDiagnostics().setClient(Client, Owner.release() != nullptr); + // FIX-ME : Change it in the Incremental Parser + // It does not work even if I call unload in IncrementalParser, I think + // it would be to early. + CodeCompletionInterp.unload(1); + + return true; + } +} + diff --git a/lib/Interpreter/InterpreterCallbacks.cpp b/lib/Interpreter/InterpreterCallbacks.cpp index 35a935cb..e476c86e 100644 --- a/lib/Interpreter/InterpreterCallbacks.cpp +++ b/lib/Interpreter/InterpreterCallbacks.cpp @@ -9,6 +9,7 @@ #include "cling/Interpreter/InterpreterCallbacks.h" +#include "cling/Interpreter/ClingTabCompletion.h" #include "cling/Interpreter/ClingCodeCompleteConsumer.h" #include "cling/Interpreter/Interpreter.h" @@ -197,6 +198,7 @@ namespace cling { m_PPCallbacks = new InterpreterPPCallbacks(this); PP.addPPCallbacks(std::unique_ptr(m_PPCallbacks)); } + m_Completer = new ClingTabCompletion(*m_Interpreter); } // pin the vtable here @@ -262,6 +264,11 @@ namespace cling { consumer->getCompletions(completions); } + void InterpreterCallbacks::CodeComplete(const std::string& line, size_t& cursor, + std::vector& displayCompletions) const { + m_Completer->Complete(line, cursor, displayCompletions); + } + void InterpreterCallbacks::UpdateWithNewDecls(const DeclContext *DC, DeclarationName Name, llvm::ArrayRef Decls) { diff --git a/lib/Interpreter/TabCompletion.h b/lib/Interpreter/TabCompletion.h new file mode 100644 index 00000000..d289b2ec --- /dev/null +++ b/lib/Interpreter/TabCompletion.h @@ -0,0 +1,30 @@ +#ifndef TABCOMPLETION_H +#define TABCOMPLETION_H + +#include "textinput/Callbacks.h" + +namespace textinput { + class TabCompletion; + class Text; + class EditorRange; +} + +namespace cling { + class TabCompletion : public textinput::TabCompletion { + const cling::Interpreter& ParentInterp; + + public: + TabCompletion(cling::Interpreter& Parent) : ParentInterp(Parent) {} + + ~TabCompletion() {} + + bool Complete(textinput::Text& Line /*in+out*/, + size_t& Cursor /*in+out*/, + textinput::EditorRange& R /*out*/, + std::vector& DisplayCompletions /*out*/) override { + const InterpreterCallbacks* callbacks = ParentInterp.getCallbacks(); + callbacks->Complete(Line.GetText(), Cursor, DisplayCompletions); + } + }; +} +#endif diff --git a/lib/UserInterface/UserInterface.cpp b/lib/UserInterface/UserInterface.cpp index 2fac9f80..b510e9e7 100644 --- a/lib/UserInterface/UserInterface.cpp +++ b/lib/UserInterface/UserInterface.cpp @@ -10,6 +10,7 @@ #include "cling/UserInterface/UserInterface.h" #include "cling/UserInterface/CompilationException.h" +#include "cling/UserInterface/TabCompletion.h" #include "cling/Interpreter/Exception.h" #include "cling/MetaProcessor/MetaProcessor.h" #include "textinput/TextInput.h" @@ -25,8 +26,6 @@ #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 @@ -121,8 +120,8 @@ namespace cling { 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); + cling::TabCompletion* Completion = new cling::TabCompletion(m_MetaProcessor->getInterpreter()); + TI.SetCompletion(Completion); TI.SetPrompt("[cling]$ ");