Skip to content

Commit 1a42ae5

Browse files
Add Code Completion
1 parent 244d1ab commit 1a42ae5

File tree

5 files changed

+95
-1
lines changed

5 files changed

+95
-1
lines changed

include/clang/Interpreter/CppInterOp.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,12 @@ namespace Cpp {
648648
CPPINTEROP_API std::string EndStdStreamCapture();
649649

650650
///@}
651+
652+
/// Append all Code completion suggestions to Results.
653+
///\param[in] code - code fragmet to complete
654+
///\param[out] Results - CC suggestions for code fragment. Suggestions are appended.
655+
CPPINTEROP_API void CodeComplete(const char* code, std::vector<std::string>& Results);
656+
651657
} // end namespace Cpp
652658

653659
#endif // CPPINTEROP_CPPINTEROP_H

lib/Interpreter/CMakeLists.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,16 @@ set(LLVM_LINK_COMPONENTS
33
BinaryFormat
44
Core
55
Object
6-
OrcJIT
6+
OrcJit
77
Support
88
)
9+
# FIXME: Investigate why this needs to be conditionally included.
10+
if ("LLVMFrontendDriver" IN_LIST LLVM_AVAILABLE_LIBS)
11+
list(APPEND LLVM_LINK_COMPONENTS FrontendDriver)
12+
endif()
13+
if ("LLVMOrcDebugging" IN_LIST LLVM_AVAILABLE_LIBS)
14+
list(APPEND LLVM_LINK_COMPONENTS OrcDebugging)
15+
endif()
916

1017
set(DLM
1118
DynamicLibraryManager.cpp

lib/Interpreter/Compatibility.h

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
#include "clang/Basic/Version.h"
1111
#include "clang/Config/config.h"
1212

13+
#if CLANG_VERSION_MAJOR >= 18
14+
#include "clang/Interpreter/CodeCompletion.h"
15+
#endif
16+
1317
#include "llvm/ADT/SmallString.h"
1418
#include "llvm/ADT/StringRef.h"
1519
#include "llvm/ADT/Twine.h"
@@ -28,6 +32,8 @@
2832

2933
#include "cling/Utils/AST.h"
3034

35+
#include <regex>
36+
3137
namespace Cpp {
3238
namespace Cpp_utils = cling::utils;
3339
}
@@ -69,6 +75,27 @@ getSymbolAddress(const cling::Interpreter& I, llvm::StringRef IRName) {
6975
Names.push_back(Jit.getExecutionSession().intern(IRName));
7076
return llvm::make_error<llvm::orc::SymbolsNotFound>(Names);
7177
}
78+
79+
inline void codeComplete(const cling::Interpreter& I, const char* code, std::vector<std::string>& Results) {
80+
std::vector<std::string> results;
81+
size_t cursor = std::string(code).size() + 1;
82+
I.codeComplete(code, cursor, results);
83+
84+
// append cleaned results
85+
for (auto& r : results) {
86+
// remove the definition at the beginning (for example [#int#])
87+
r = std::regex_replace(r, std::regex("\\[\\#.*\\#\\]"), "");
88+
// remove the variable name in <#type name#>
89+
r = std::regex_replace(r, std::regex("(\\ |\\*)+(\\w+)(\\#\\>)"), "$1$3");
90+
// remove unnecessary space at the end of <#type #>
91+
r = std::regex_replace(r, std::regex("\\ *(\\#\\>)"), "$1");
92+
// remove <# #> to keep only the type
93+
r = std::regex_replace(r, std::regex("\\<\\#([^#>]*)\\#\\>"), "$1");
94+
if (r.find(code) == 0)
95+
Results.push_back(r);
96+
}
97+
}
98+
7299
} // namespace compat
73100

74101
#endif // USE_CLING
@@ -184,6 +211,7 @@ inline void maybeMangleDeclName(const clang::GlobalDecl& GD,
184211
// Clang 14 - Add new Interpreter methods: getExecutionEngine,
185212
// getSymbolAddress, getSymbolAddressFromLinkerName
186213
// Clang 15 - Add new Interpreter methods: Undo
214+
// Clang 18 - Add new Interpreter methods: CodeComplete
187215

188216
inline llvm::orc::LLJIT* getExecutionEngine(clang::Interpreter& I) {
189217
#if CLANG_VERSION_MAJOR >= 14
@@ -247,6 +275,34 @@ inline llvm::Error Undo(clang::Interpreter& I, unsigned N = 1) {
247275
#endif
248276
}
249277

278+
inline void codeComplete(clang::Interpreter& I, const char* code, std::vector<std::string>& Results) {
279+
#if CLANG_VERSION_MAJOR >= 18
280+
auto CB = clang::IncrementalCompilerBuilder();
281+
auto CI = CB.CreateCpp();
282+
if (auto Err = CI.takeError()) {
283+
llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
284+
return;
285+
}
286+
auto Interp = clang::Interpreter::create(std::move(*CI));
287+
if (auto Err = Interp.takeError()) {
288+
llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
289+
return;
290+
}
291+
292+
std::vector<std::string> results;
293+
std::vector<std::string> Comps;
294+
clang::CompilerInstance *MainCI = (*Interp)->getCompilerInstance();
295+
auto CC = clang::ReplCodeCompleter();
296+
CC.codeComplete(MainCI, code, /* Lines */ 1, std::string(code).size() + 1,
297+
I.getCompilerInstance(), results);
298+
for (auto r : results)
299+
if (r.find(CC.Prefix) == 0)
300+
Results.push_back(r);
301+
#else
302+
assert(false && "CodeCompletion API only available in Clang >= 18.");
303+
#endif
304+
}
305+
250306
} // namespace compat
251307

252308
#include "CppInterOpInterpreter.h"

lib/Interpreter/CppInterOp.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3257,4 +3257,9 @@ namespace Cpp {
32573257
return result;
32583258
}
32593259

3260+
void CodeComplete(const char* code, std::vector<std::string>& Results) {
3261+
compat::codeComplete(getInterp(), code, Results);
3262+
}
3263+
3264+
32603265
} // end namespace Cpp

unittests/CppInterOp/InterpreterTest.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
#include "clang/Interpreter/CppInterOp.h"
22

3+
#include "clang/Basic/Version.h"
4+
35
#include "llvm/ADT/SmallString.h"
46
#include "llvm/Support/Path.h"
57

68
#include <gmock/gmock.h>
79
#include "gtest/gtest.h"
810

11+
#include <algorithm>
12+
913
using ::testing::StartsWith;
1014

1115
TEST(InterpreterTest, Version) {
@@ -103,3 +107,19 @@ TEST(InterpreterTest, DetectSystemCompilerIncludePaths) {
103107
Cpp::DetectSystemCompilerIncludePaths(includes);
104108
EXPECT_FALSE(includes.empty());
105109
}
110+
111+
TEST(InterpreterTest, CodeCompletion) {
112+
#if CLANG_VERSION_MAJOR >= 18 || USE_CLING
113+
std::vector<std::string> cc;
114+
Cpp::Declare("int foo = 12;");
115+
Cpp::CodeComplete("f", cc);
116+
size_t cnt = 0;
117+
for (auto& r : cc) {
118+
if (r == "float" || r == "foo") cnt++;
119+
printf("!!!%s", r.c_str());
120+
}
121+
EXPECT_EQ(2U, cnt); // float and foo
122+
#else
123+
GTEST_SKIP();
124+
#endif
125+
}

0 commit comments

Comments
 (0)