Some update.

This commit is contained in:
ykiko
2024-10-27 13:31:26 +08:00
parent 10071e3b0a
commit e1afd40663
14 changed files with 333 additions and 84 deletions

View File

@@ -2,7 +2,7 @@
# compatible with clang-format 18
UseTab: Never
ColumnLimit: 120
ColumnLimit: 100
# Indent
IndentWidth: 4

11
.vscode/launch.json vendored
View File

@@ -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
View 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
View 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

View 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

View File

@@ -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();

View 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

View 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

View File

@@ -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() {

View File

@@ -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;
}

View File

@@ -1,3 +1,3 @@
inline void bar() {
fo
// fo
}

View 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

View File

@@ -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