Some update.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
# compatible with clang-format 18
|
||||
|
||||
UseTab: Never
|
||||
ColumnLimit: 120
|
||||
ColumnLimit: 100
|
||||
|
||||
# Indent
|
||||
IndentWidth: 4
|
||||
|
||||
11
.vscode/launch.json
vendored
11
.vscode/launch.json
vendored
@@ -38,6 +38,17 @@
|
||||
],
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "CodeCompletion",
|
||||
"program": "${workspaceFolder}/build/bin/clice-tests",
|
||||
"args": [
|
||||
"--test-dir=/home/ykiko/C++/clice2/tests",
|
||||
"--gtest_filter=clice.CodeCompletion"
|
||||
],
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
|
||||
39
include/Basic/Basic.h
Normal file
39
include/Basic/Basic.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
#include <llvm/ADT/StringExtras.h>
|
||||
|
||||
namespace clice::proto {
|
||||
|
||||
/// range in [-2^31, 2^31- 1]
|
||||
using integer = std::int32_t;
|
||||
|
||||
/// range in [0, 2^31- 1]
|
||||
using uinteger = std::uint32_t;
|
||||
|
||||
using string = std::string;
|
||||
|
||||
using string_literal = llvm::StringLiteral;
|
||||
|
||||
template <typename T>
|
||||
using array = std::vector<T>;
|
||||
|
||||
using DocumentUri = std::string;
|
||||
|
||||
// TODO: figure out URI.
|
||||
using URI = std::string;
|
||||
|
||||
/// Beacuse C++ does support string enum, so define `enum_type` for
|
||||
/// tag when serialize/deserialize.
|
||||
template <typename T>
|
||||
struct enum_type {
|
||||
T value;
|
||||
|
||||
using underlying_type = T;
|
||||
|
||||
constexpr enum_type(T value) : value(value) {}
|
||||
|
||||
friend bool operator== (const enum_type& lhs, const enum_type& rhs) = default;
|
||||
};
|
||||
|
||||
} // namespace clice::proto
|
||||
42
include/Basic/Location.h
Normal file
42
include/Basic/Location.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <Basic/Basic.h>
|
||||
|
||||
namespace clice::proto {
|
||||
|
||||
/// A set of predefined position encoding kinds.
|
||||
struct PositionEncodingKind : enum_type<string_literal> {
|
||||
using enum_type::enum_type;
|
||||
constexpr inline static string_literal UTF8 = "utf-8";
|
||||
constexpr inline static string_literal UTF16 = "utf-16";
|
||||
constexpr inline static string_literal UTF32 = "utf-32";
|
||||
};
|
||||
|
||||
struct Position {
|
||||
/// Line position in a document (zero-based).
|
||||
uinteger line;
|
||||
|
||||
/// Character offset on a line in a document (zero-based).
|
||||
/// The meaning of this offset is determined by the negotiated `PositionEncodingKind`.
|
||||
uinteger character;
|
||||
};
|
||||
|
||||
struct Range {
|
||||
/// The range's start position.
|
||||
Position start;
|
||||
|
||||
/// The range's end position.
|
||||
Position end;
|
||||
};
|
||||
|
||||
struct TextEdit {
|
||||
/// The range of the text document to be manipulated. To insert
|
||||
/// text into a document create a range where start === end.
|
||||
Range range;
|
||||
|
||||
// The string to be inserted. For delete operations use an
|
||||
// empty string.
|
||||
string newText;
|
||||
};
|
||||
|
||||
} // namespace clice::proto
|
||||
30
include/Basic/SourceCode.h
Normal file
30
include/Basic/SourceCode.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <Basic/Location.h>
|
||||
#include <clang/Basic/SourceLocation.h>
|
||||
|
||||
namespace clice {
|
||||
|
||||
/// Convert a clang::SourceLocation to a proto::Position according to the
|
||||
/// specified encoding kind. Note that `SourceLocation` in clang is one-based and
|
||||
/// is always encoded in UTF-8.
|
||||
proto::Position toPosition(llvm::StringRef content,
|
||||
clang::SourceLocation location,
|
||||
proto::PositionEncodingKind kind,
|
||||
const clang::SourceManager& srcMgr);
|
||||
|
||||
/// Same as above, but for a group of locations. It is more efficient than calling
|
||||
/// `toLocation` multiple times. Note that the locations must be sorted.
|
||||
std::vector<proto::Position> toPosition(llvm::StringRef content,
|
||||
llvm::ArrayRef<clang::SourceLocation> locations,
|
||||
proto::PositionEncodingKind kind,
|
||||
const clang::SourceManager& srcMgr);
|
||||
|
||||
/// Convert a proto::Position to a clang::SourceLocation according to the
|
||||
/// specified encoding kind. If any error occurs, return an invalid location.
|
||||
clang::SourceLocation toSourceLocation(llvm::StringRef content,
|
||||
proto::Position position,
|
||||
proto::PositionEncodingKind kind,
|
||||
const clang::SourceManager& srcMgr);
|
||||
|
||||
} // namespace clice
|
||||
@@ -27,8 +27,9 @@ public:
|
||||
/// Build AST.
|
||||
void buildAST();
|
||||
|
||||
/// Generate the PCH(PreCompiledHeader) to output path. Generally execute `clang::GeneratePCHAction`.
|
||||
/// The Header part of the source file is stored in the PCH file. Bound is the size of the header part.
|
||||
/// Generate the PCH(PreCompiledHeader) to output path. Generally execute
|
||||
/// `clang::GeneratePCHAction`. The Header part of the source file is stored in the PCH file.
|
||||
/// Bound is the size of the header part.
|
||||
void generatePCH(llvm::StringRef outpath, std::uint32_t bound, bool endAtStart = false);
|
||||
|
||||
/// Generate the PCM(PreCompiledModule) to output path. Generally execute
|
||||
@@ -36,10 +37,7 @@ public:
|
||||
void generatePCM(llvm::StringRef outpath);
|
||||
|
||||
/// Run code complete in given file and location.
|
||||
void codeCompletion(llvm::StringRef filepath,
|
||||
std::uint32_t line,
|
||||
std::uint32_t column,
|
||||
clang::CodeCompleteConsumer* consumer);
|
||||
void codeCompletion(llvm::StringRef filepath, std::uint32_t line, std::uint32_t column);
|
||||
|
||||
clang::Preprocessor& pp() {
|
||||
return instance->getPreprocessor();
|
||||
|
||||
86
include/Feature/CodeCompletion.h
Normal file
86
include/Feature/CodeCompletion.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
|
||||
#include <Basic/Location.h>
|
||||
|
||||
namespace clice::proto {
|
||||
|
||||
struct CompletionClientCapabilities {};
|
||||
|
||||
enum CompletionItemKind {
|
||||
Text = 1,
|
||||
Method = 2,
|
||||
Function = 3,
|
||||
Constructor = 4,
|
||||
Field = 5,
|
||||
Variable = 6,
|
||||
Class = 7,
|
||||
Interface = 8,
|
||||
Module = 9,
|
||||
Property = 10,
|
||||
Unit = 11,
|
||||
Value = 12,
|
||||
Enum = 13,
|
||||
Keyword = 14,
|
||||
Snippet = 15,
|
||||
Color = 16,
|
||||
File = 17,
|
||||
Reference = 18,
|
||||
Folder = 19,
|
||||
EnumMember = 20,
|
||||
Constant = 21,
|
||||
Struct = 22,
|
||||
Event = 23,
|
||||
Operator = 24,
|
||||
TypeParameter,
|
||||
};
|
||||
|
||||
enum CompletionItemTag {
|
||||
/// Render a completion as obsolete, usually using a strike-out.
|
||||
Deprecated = 1,
|
||||
};
|
||||
|
||||
struct CompletionItem {
|
||||
/// The label of this completion item.
|
||||
/// If label details are provided the label itself should
|
||||
/// be an unqualified name of the completion item.
|
||||
string label;
|
||||
|
||||
/// FIXME:
|
||||
/// labelDetails?: CompletionItemLabelDetails;
|
||||
|
||||
// The kind of this completion item. Based of the kind
|
||||
// an icon is chosen by the editor. The standardized set
|
||||
// of available values is defined in `CompletionItemKind`.
|
||||
CompletionItemKind kind;
|
||||
|
||||
/// Tags for this completion item.
|
||||
std::vector<CompletionItemTag> tags;
|
||||
|
||||
// FIXME:
|
||||
// ...
|
||||
};
|
||||
|
||||
} // namespace clice::proto
|
||||
|
||||
namespace clice {
|
||||
class Compiler;
|
||||
}
|
||||
|
||||
namespace clice::config {
|
||||
|
||||
struct CodeCompletionOption {
|
||||
// TODO:
|
||||
};
|
||||
|
||||
} // namespace clice::config
|
||||
|
||||
namespace clice::feature {
|
||||
|
||||
/// Run code completion in given file and location. `compiler` should be
|
||||
/// set properly if any PCH or PCM is needed.
|
||||
std::vector<proto::CompletionItem> codeCompletion(Compiler& compiler,
|
||||
llvm::StringRef filepath,
|
||||
proto::Position position,
|
||||
const config::CodeCompletionOption& option);
|
||||
|
||||
} // namespace clice::feature
|
||||
74
src/Compiler/CodeCompletion.cpp
Normal file
74
src/Compiler/CodeCompletion.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#include <Compiler/CodeComplete.h>
|
||||
#include <clang/Lex/CodeCompletionHandler.h>
|
||||
|
||||
namespace clice {
|
||||
|
||||
namespace {
|
||||
|
||||
class CodeCompleteConsumer final : public clang::CodeCompleteConsumer {
|
||||
public:
|
||||
CodeCompleteConsumer(clang::CodeCompleteOptions options) :
|
||||
clang::CodeCompleteConsumer(options), allocator(new clang::GlobalCodeCompletionAllocator()),
|
||||
info(allocator) {
|
||||
// TODO:
|
||||
}
|
||||
|
||||
void ProcessCodeCompleteResults(clang::Sema& sema,
|
||||
clang::CodeCompletionContext context,
|
||||
clang::CodeCompletionResult* results,
|
||||
unsigned count) final {
|
||||
sema.getPreprocessor().getCodeCompletionLoc();
|
||||
for(auto& result: llvm::make_range(results, results + count)) {
|
||||
llvm::outs() << "Kind: " << result.Kind << " ";
|
||||
switch(result.Kind) {
|
||||
case clang::CodeCompletionResult::RK_Declaration: {
|
||||
result.getDeclaration()->dump();
|
||||
break;
|
||||
}
|
||||
case clang::CodeCompletionResult::RK_Keyword: {
|
||||
llvm::outs() << result.Keyword << "\n";
|
||||
break;
|
||||
}
|
||||
case clang::CodeCompletionResult::RK_Macro: {
|
||||
llvm::outs() << result.Macro << "\n";
|
||||
break;
|
||||
}
|
||||
case clang::CodeCompletionResult::RK_Pattern: {
|
||||
llvm::outs() << result.Pattern->getAsString() << "\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessOverloadCandidates(clang::Sema& sema,
|
||||
unsigned CurrentArg,
|
||||
OverloadCandidate* candidates,
|
||||
unsigned count,
|
||||
clang::SourceLocation openParLoc,
|
||||
bool braced) final {
|
||||
llvm::outs() << "ProcessOverloadCandidates\n";
|
||||
auto range = llvm::make_range(candidates, candidates + count);
|
||||
for(auto& candidate: range) {
|
||||
switch(candidate.getKind()) {
|
||||
// case
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clang::CodeCompletionAllocator& getAllocator() final {
|
||||
return *allocator;
|
||||
}
|
||||
|
||||
clang::CodeCompletionTUInfo& getCodeCompletionTUInfo() final {
|
||||
return info;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<clang::GlobalCodeCompletionAllocator> allocator;
|
||||
clang::CodeCompletionTUInfo info;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace clice
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <Compiler/Compiler.h>
|
||||
#include <clang/Lex/PreprocessorOptions.h>
|
||||
#include <clang/Frontend/TextDiagnosticPrinter.h>
|
||||
#include <Compiler/CodeComplete.h>
|
||||
|
||||
namespace clice {
|
||||
|
||||
@@ -16,7 +17,8 @@ Compiler::Compiler(llvm::StringRef filepath,
|
||||
llvm::StringRef content,
|
||||
llvm::ArrayRef<const char*> args,
|
||||
clang::DiagnosticConsumer* consumer,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs) : filepath(filepath), content(content) {
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs) :
|
||||
filepath(filepath), content(content) {
|
||||
// FIXME: figure out should we use createInvocation?
|
||||
clang::CreateInvocationOptions options;
|
||||
auto invocation = clang::createInvocation(args, options);
|
||||
@@ -29,8 +31,9 @@ Compiler::Compiler(llvm::StringRef filepath,
|
||||
if(consumer) {
|
||||
instance->createDiagnostics(consumer, true);
|
||||
} else {
|
||||
instance->createDiagnostics(new clang::TextDiagnosticPrinter(llvm::outs(), new clang::DiagnosticOptions()),
|
||||
true);
|
||||
instance->createDiagnostics(
|
||||
new clang::TextDiagnosticPrinter(llvm::outs(), new clang::DiagnosticOptions()),
|
||||
true);
|
||||
}
|
||||
|
||||
if(!instance->createTarget()) {
|
||||
@@ -74,15 +77,11 @@ void Compiler::generatePCM(llvm::StringRef outpath) {
|
||||
ExecuteAction();
|
||||
}
|
||||
|
||||
void Compiler::codeCompletion(llvm::StringRef filepath,
|
||||
std::uint32_t line,
|
||||
std::uint32_t column,
|
||||
clang::CodeCompleteConsumer* consumer) {
|
||||
void Compiler::codeCompletion(llvm::StringRef filepath, std::uint32_t line, std::uint32_t column) {
|
||||
auto& completion = instance->getFrontendOpts().CodeCompletionAt;
|
||||
completion.FileName = filepath;
|
||||
completion.Line = line;
|
||||
completion.Column = column;
|
||||
instance->setCodeCompletionConsumer(consumer);
|
||||
buildAST();
|
||||
}
|
||||
|
||||
@@ -102,7 +101,8 @@ void Compiler::ExecuteAction() {
|
||||
|
||||
// Beacuse CompilerInstance may create new Preprocessor in `BeginSourceFile`,
|
||||
// So we must need to create TokenCollector here.
|
||||
clang::syntax::TokenCollector collector{preproc};
|
||||
// clang::syntax::TokenCollector collector{preproc};
|
||||
CodeCompleteCollector collect2(*instance);
|
||||
|
||||
// FIXME: clang-tidy, include-fixer, etc?
|
||||
|
||||
@@ -112,8 +112,8 @@ void Compiler::ExecuteAction() {
|
||||
}
|
||||
|
||||
// Build TokenBuffer and index expanded tokens for improving performance.
|
||||
buffer = std::make_unique<clang::syntax::TokenBuffer>(std::move(collector).consume());
|
||||
buffer->indexExpandedTokens();
|
||||
// buffer = std::make_unique<clang::syntax::TokenBuffer>(std::move(collector).consume());
|
||||
// buffer->indexExpandedTokens();
|
||||
}
|
||||
|
||||
Compiler::~Compiler() {
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
#include "test1.h"
|
||||
#include "test2.h"
|
||||
struct X {
|
||||
int x;
|
||||
};
|
||||
|
||||
void foo(int x, int y);
|
||||
|
||||
int main() {
|
||||
X xxx;
|
||||
xxx.x = 2;
|
||||
foo()
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
inline void bar() {
|
||||
fo
|
||||
// fo
|
||||
}
|
||||
|
||||
26
unittests/AST/Completion.cpp
Normal file
26
unittests/AST/Completion.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "../Test.h"
|
||||
#include <AST/Selection.h>
|
||||
#include <Compiler/Compiler.h>
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace clice;
|
||||
|
||||
TEST(clice, CodeCompletion) {
|
||||
foreachFile("CodeCompletion", [](std::string file, llvm::StringRef content) {
|
||||
if(file.ends_with("test.cpp")) {
|
||||
std::vector<const char*> compileArgs = {
|
||||
"clang++",
|
||||
"-std=c++20",
|
||||
file.c_str(),
|
||||
"-resource-dir",
|
||||
"/home/ykiko/C++/clice2/build/lib/clang/20",
|
||||
};
|
||||
auto compiler = Compiler(compileArgs);
|
||||
compiler.codeCompletion(file, 10, 9);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <Compiler/Compiler.h>
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace clice;
|
||||
|
||||
std::vector<const char*> compileArgs = {
|
||||
"clang++",
|
||||
"-std=c++20",
|
||||
"/home/ykiko/C++/clice2/tests/Source/CodeCompletion/test.cpp",
|
||||
"-resource-dir",
|
||||
"/home/ykiko/C++/clice2/build/lib/clang/20",
|
||||
};
|
||||
|
||||
class CodeCompletionConsumer : public clang::CodeCompleteConsumer {
|
||||
public:
|
||||
void ProcessCodeCompleteResults(clang::Sema& S,
|
||||
clang::CodeCompletionContext Context,
|
||||
clang::CodeCompletionResult* Results,
|
||||
unsigned NumResults) override {
|
||||
for(unsigned i = 0; i < NumResults; ++i) {
|
||||
auto str =
|
||||
Results[i].CreateCodeCompletionString(S, Context, getAllocator(), getCodeCompletionTUInfo(), true);
|
||||
llvm::outs() << str->getAsString() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessOverloadCandidates(clang::Sema& S,
|
||||
unsigned CurrentArg,
|
||||
OverloadCandidate* Candidates,
|
||||
unsigned NumCandidates,
|
||||
clang::SourceLocation OpenParLoc,
|
||||
bool Braced) override {}
|
||||
|
||||
clang::CodeCompletionAllocator& getAllocator() override {
|
||||
return *Allocator;
|
||||
};
|
||||
|
||||
clang::CodeCompletionTUInfo& getCodeCompletionTUInfo() override {
|
||||
return TUInfo;
|
||||
}
|
||||
|
||||
CodeCompletionConsumer() :
|
||||
clang::CodeCompleteConsumer(clang::CodeCompleteOptions()),
|
||||
Allocator(std::make_shared<clang::GlobalCodeCompletionAllocator>()), TUInfo(Allocator) {}
|
||||
|
||||
private:
|
||||
std::shared_ptr<clang::GlobalCodeCompletionAllocator> Allocator;
|
||||
clang::CodeCompletionTUInfo TUInfo;
|
||||
};
|
||||
|
||||
TEST(clice, CodeCompletion) {
|
||||
auto invocation = clang::createInvocation(compileArgs, {});
|
||||
|
||||
Compiler compiler(compileArgs);
|
||||
compiler.codeCompletion("/home/ykiko/C++/clice2/tests/Source/CodeCompletion/test.cpp",
|
||||
2,
|
||||
7,
|
||||
new CodeCompletionConsumer());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user