Implement SourceConverter (#26)
This commit is contained in:
@@ -21,7 +21,4 @@ using array = std::vector<T>;
|
||||
|
||||
using DocumentUri = std::string;
|
||||
|
||||
// TODO: figure out URI.
|
||||
using URI = std::string;
|
||||
|
||||
} // namespace clice::proto
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <Basic/Location.h>
|
||||
#include <clang/Basic/SourceLocation.h>
|
||||
|
||||
namespace clice {
|
||||
|
||||
/// Measure the length of the content with the specified encoding kind.
|
||||
std::size_t remeasure(llvm::StringRef content, proto::PositionEncodingKind kind);
|
||||
|
||||
/// Convert a clang::SourceLocation to a proto::Position according to the
|
||||
/// specified encoding kind. Note that `SourceLocation` in clang is 1-based and
|
||||
/// is always encoded in UTF-8.
|
||||
proto::Position toPosition(llvm::StringRef content,
|
||||
clang::SourceLocation location,
|
||||
proto::PositionEncodingKind kind,
|
||||
const clang::SourceManager& SM);
|
||||
|
||||
/// Same as above, but content is retrieved from the `SourceManager`.
|
||||
proto::Position toPosition(clang::SourceLocation location,
|
||||
proto::PositionEncodingKind kind,
|
||||
const clang::SourceManager& SM);
|
||||
|
||||
/// Convert a proto::Position to a file offset in the content with the specified
|
||||
/// encoding kind.
|
||||
std::size_t toOffset(llvm::StringRef content,
|
||||
proto::Position position,
|
||||
proto::PositionEncodingKind kind);
|
||||
|
||||
} // namespace clice
|
||||
68
include/Basic/SourceConverter.h
Normal file
68
include/Basic/SourceConverter.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include "llvm/Support/Error.h"
|
||||
|
||||
#include <Basic/Location.h>
|
||||
#include <clang/Basic/SourceLocation.h>
|
||||
|
||||
namespace clice {
|
||||
|
||||
/// A helper class to convert `Position, Range, Location` between 1-1 encoding based clang and 0-0
|
||||
/// encoding based LSP. The conversion of DocumentUri is also supported.
|
||||
class SourceConverter {
|
||||
public:
|
||||
/// [(origin, new)], map origin header directory to another source directory.
|
||||
using SourceDirMapping = std::vector<std::pair<std::string, std::string>>;
|
||||
|
||||
/// Construct a `SourceConverter` with the specified encoding kind and empty source map.
|
||||
explicit SourceConverter(proto::PositionEncodingKind kind) : kind(kind), sourceMap() {}
|
||||
|
||||
SourceConverter(proto::PositionEncodingKind kind, SourceDirMapping sourceMap) :
|
||||
kind(kind), sourceMap(std::move(sourceMap)) {}
|
||||
|
||||
SourceConverter(const SourceConverter&) = delete;
|
||||
|
||||
SourceConverter(SourceConverter&&) = default;
|
||||
|
||||
/// Measure the length (character count) of the content with the specified encoding kind.
|
||||
std::size_t remeasure(llvm::StringRef content) const;
|
||||
|
||||
/// Convert a clang::SourceLocation to a proto::Position according to the
|
||||
/// specified encoding kind. Note that `SourceLocation` in clang is 1-based and
|
||||
/// is always encoded in UTF-8.
|
||||
proto::Position toPosition(llvm::StringRef content, clang::SourceLocation location,
|
||||
const clang::SourceManager& SM) const;
|
||||
|
||||
/// Same as above, but content is retrieved from the `SourceManager`.
|
||||
proto::Position toPosition(clang::SourceLocation location,
|
||||
const clang::SourceManager& SM) const;
|
||||
|
||||
/// Convert a clang::SourceRange to a proto::Range according to the specified encoding kind.
|
||||
proto::Range toRange(clang::SourceRange range, const clang::SourceManager& SM) const {
|
||||
return {toPosition(range.getBegin(), SM), toPosition(range.getEnd(), SM)};
|
||||
}
|
||||
|
||||
/// Convert a proto::Position to a file offset in the content with the specified
|
||||
/// encoding kind.
|
||||
std::size_t toOffset(llvm::StringRef content, proto::Position position) const;
|
||||
|
||||
/// Get the encoding kind of the content in LSP protocol.
|
||||
proto::PositionEncodingKind encodingKind() const {
|
||||
return kind;
|
||||
}
|
||||
|
||||
/// Convert a real path of a file to URI. Crash if failed.
|
||||
static proto::DocumentUri toURI(llvm::StringRef fspath);
|
||||
|
||||
/// Convert a file URI to real path with `clice::fs::real_path`. Crash if failed.
|
||||
static std::string toPath(llvm::StringRef uri);
|
||||
|
||||
private:
|
||||
/// The encoding kind of the content in LSP protocol.
|
||||
proto::PositionEncodingKind kind;
|
||||
|
||||
/// A user-defined map from header file to its source directory.
|
||||
SourceDirMapping sourceMap;
|
||||
};
|
||||
|
||||
} // namespace clice
|
||||
@@ -1,52 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Support/Support.h"
|
||||
|
||||
namespace clice {
|
||||
|
||||
class URI {
|
||||
public:
|
||||
URI(llvm::StringRef scheme, llvm::StringRef authority, llvm::StringRef path) :
|
||||
m_scheme(scheme), m_authority(authority), m_body(path) {}
|
||||
|
||||
URI(const URI&) = default;
|
||||
|
||||
bool operator== (const URI&) const = default;
|
||||
|
||||
/// Construct a URI object from the given file path.
|
||||
static URI from(llvm::StringRef file);
|
||||
|
||||
/// Parse the given URI string to create a URI object.
|
||||
static llvm::Expected<URI> parse(llvm::StringRef content);
|
||||
|
||||
/// Same as `parse`, but will crash if failed.
|
||||
static std::string resolve(llvm::StringRef content);
|
||||
|
||||
/// Returns decoded scheme e.g. "https"
|
||||
llvm::StringRef scheme() const {
|
||||
return m_scheme;
|
||||
}
|
||||
|
||||
/// Returns decoded authority e.g. "reviews.llvm.org"
|
||||
llvm::StringRef authority() const {
|
||||
return m_authority;
|
||||
}
|
||||
|
||||
/// Returns decoded body e.g. "/D41946"
|
||||
llvm::StringRef body() const {
|
||||
return m_body;
|
||||
}
|
||||
|
||||
std::string toString() const {
|
||||
return std::format("{}://{}{}", m_scheme, m_authority, m_body);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_scheme;
|
||||
std::string m_authority;
|
||||
std::string m_body;
|
||||
};
|
||||
|
||||
} // namespace clice
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "Basic/Document.h"
|
||||
#include "Basic/SourceConverter.h"
|
||||
#include "Compiler/Compiler.h"
|
||||
|
||||
namespace clice {
|
||||
@@ -21,7 +22,6 @@ struct FoldingRangeClientCapabilities {};
|
||||
/// ```
|
||||
|
||||
struct FoldingRangeParams {
|
||||
|
||||
/// The text document.
|
||||
TextDocumentIdentifier textDocument;
|
||||
};
|
||||
@@ -68,12 +68,11 @@ using FoldingRangeResult = std::vector<FoldingRange>;
|
||||
|
||||
namespace feature {
|
||||
|
||||
/// TODO:
|
||||
/// use `proto::FoldingRangeClientCapabilities` instead of `json::Value`to make a proper overload.
|
||||
// json::Value capability(json::Value FoldingRangeClientCapabilities);
|
||||
json::Value foldingRangeCapability(json::Value foldingRangeClientCapabilities);
|
||||
|
||||
/// Return folding range in given file.
|
||||
proto::FoldingRangeResult foldingRange(FoldingRangeParams& params, ASTInfo& ast);
|
||||
proto::FoldingRangeResult foldingRange(FoldingRangeParams& params, ASTInfo& ast,
|
||||
const SourceConverter& converter);
|
||||
|
||||
} // namespace feature
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "Basic/URI.h"
|
||||
|
||||
#include "Feature/Lookup.h"
|
||||
#include "Feature/DocumentHighlight.h"
|
||||
#include "Feature/DocumentLink.h"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "Basic/SourceCode.h"
|
||||
#include "Basic/SourceConverter.h"
|
||||
#include "Basic/Location.h"
|
||||
|
||||
namespace clice {
|
||||
|
||||
@@ -60,7 +61,7 @@ static bool iterateCodepoints(llvm::StringRef content, const Callback& callback)
|
||||
return false;
|
||||
}
|
||||
|
||||
std::size_t remeasure(llvm::StringRef content, proto::PositionEncodingKind kind) {
|
||||
std::size_t SourceConverter::remeasure(llvm::StringRef content) const {
|
||||
if(kind == proto::PositionEncodingKind::UTF8) {
|
||||
return content.size();
|
||||
}
|
||||
@@ -86,10 +87,8 @@ std::size_t remeasure(llvm::StringRef content, proto::PositionEncodingKind kind)
|
||||
std::unreachable();
|
||||
}
|
||||
|
||||
proto::Position toPosition(llvm::StringRef content,
|
||||
clang::SourceLocation location,
|
||||
proto::PositionEncodingKind kind,
|
||||
const clang::SourceManager& SM) {
|
||||
proto::Position SourceConverter::toPosition(llvm::StringRef content, clang::SourceLocation location,
|
||||
const clang::SourceManager& SM) const {
|
||||
assert(location.isValid() && location.isFileID() &&
|
||||
"SourceLocation must be valid and not a macro location");
|
||||
auto [fileID, offset] = SM.getDecomposedSpellingLoc(location);
|
||||
@@ -101,23 +100,24 @@ proto::Position toPosition(llvm::StringRef content,
|
||||
proto::Position position;
|
||||
/// Line doesn't need to be adjusted. It is encoding-dependent.
|
||||
position.line = line;
|
||||
|
||||
/// Column needs to be adjusted based on the encoding.
|
||||
position.character = remeasure(content.substr(offset - column, column), kind);
|
||||
if(auto word = content.substr(offset - column, column); !word.empty())
|
||||
position.character = remeasure(word);
|
||||
else
|
||||
position.character = column; // word is the last column of that line.
|
||||
return position;
|
||||
}
|
||||
|
||||
proto::Position toPosition(clang::SourceLocation location,
|
||||
proto::PositionEncodingKind kind,
|
||||
const clang::SourceManager& SM) {
|
||||
proto::Position SourceConverter::toPosition(clang::SourceLocation location,
|
||||
const clang::SourceManager& SM) const {
|
||||
bool isInvalid = false;
|
||||
llvm::StringRef content = SM.getCharacterData(location, &isInvalid);
|
||||
assert(!isInvalid && "Invalid SourceLocation");
|
||||
return toPosition(content, location, kind, SM);
|
||||
return toPosition(content, location, SM);
|
||||
}
|
||||
|
||||
std::size_t toOffset(llvm::StringRef content,
|
||||
proto::Position position,
|
||||
proto::PositionEncodingKind kind) {
|
||||
std::size_t SourceConverter::toOffset(llvm::StringRef content, proto::Position position) const {
|
||||
std::size_t offset = 0;
|
||||
for(auto i = 0; i < position.line; i++) {
|
||||
auto pos = content.find('\n');
|
||||
@@ -159,4 +159,81 @@ std::size_t toOffset(llvm::StringRef content,
|
||||
std::unreachable();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/// decodes a string according to percent-encoding, e.g., "a%20b" -> "a b".
|
||||
static std::string decodePercent(llvm::StringRef content) {
|
||||
std::string result;
|
||||
result.reserve(content.size());
|
||||
|
||||
for(auto iter = content.begin(), send = content.end(); iter != send; ++iter) {
|
||||
auto c = *iter;
|
||||
if(c == '%' && iter + 2 < send) {
|
||||
auto m = *(iter + 1);
|
||||
auto n = *(iter + 2);
|
||||
if(llvm::isHexDigit(m) && llvm::isHexDigit(n)) {
|
||||
result += llvm::hexFromNibbles(m, n);
|
||||
iter += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
result += c;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
proto::DocumentUri SourceConverter::toURI(llvm::StringRef fspath) {
|
||||
if(!path::is_absolute(fspath))
|
||||
std::terminate();
|
||||
|
||||
llvm::SmallString<128> path("file://");
|
||||
|
||||
for(auto c: fspath) {
|
||||
if(c == '\\') {
|
||||
path.push_back('/');
|
||||
} else if(std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '/') {
|
||||
path.push_back(c);
|
||||
} else {
|
||||
path.push_back('%');
|
||||
path.push_back(llvm::hexdigit(c >> 4));
|
||||
path.push_back(llvm::hexdigit(c & 0xF));
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO:
|
||||
/// use `sourceMap` to replace prefix with mapped path.
|
||||
// for(const auto& [prefix, newPrefix]: sourceMap) {
|
||||
// if(fspath.starts_with(prefix)) {
|
||||
// path.append(newPrefix); // todo: newPrefix.end_with('/') ???
|
||||
// path.append(fspath.substr(prefix.size()));
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
return path.str().str();
|
||||
};
|
||||
|
||||
std::string SourceConverter::toPath(llvm::StringRef uri) {
|
||||
llvm::StringRef cloned = uri;
|
||||
|
||||
auto pos = cloned.find(':');
|
||||
if(pos == llvm::StringRef::npos)
|
||||
std::terminate();
|
||||
|
||||
auto scheme = cloned.substr(0, pos);
|
||||
cloned = cloned.substr(pos + 1);
|
||||
|
||||
if(cloned.consume_front("//"))
|
||||
cloned = cloned.substr(cloned.find('/'));
|
||||
|
||||
auto decoded = decodePercent(cloned);
|
||||
llvm::SmallString<128> result;
|
||||
if(auto err = fs::real_path(decoded, result))
|
||||
std::terminate();
|
||||
|
||||
return result.str().str();
|
||||
}
|
||||
|
||||
} // namespace clice
|
||||
@@ -1,105 +0,0 @@
|
||||
#include <Basic/URI.h>
|
||||
|
||||
#include <Support/ADT.h>
|
||||
#include <Support/FileSystem.h>
|
||||
|
||||
namespace clice {
|
||||
|
||||
namespace {
|
||||
|
||||
/// returns true if the scheme is valid according to RFC 3986.
|
||||
bool isValidScheme(llvm::StringRef scheme) {
|
||||
if(scheme.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!llvm::isAlpha(scheme[0])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return llvm::all_of(llvm::drop_begin(scheme), [](char C) {
|
||||
return llvm::isAlnum(C) || C == '+' || C == '.' || C == '-';
|
||||
});
|
||||
}
|
||||
|
||||
/// decodes a string according to percent-encoding, e.g., "a%20b" -> "a b".
|
||||
static std::string decodePercent(llvm::StringRef content) {
|
||||
std::string result;
|
||||
for(auto iter = content.begin(), sent = content.end(); iter != sent; ++iter) {
|
||||
auto c = *iter;
|
||||
if(c == '%' && iter + 2 < sent) {
|
||||
auto m = *(iter + 1);
|
||||
auto n = *(iter + 2);
|
||||
if(llvm::isHexDigit(m) && llvm::isHexDigit(n)) {
|
||||
result += llvm::hexFromNibbles(m, n);
|
||||
iter += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
result += c;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
URI URI::from(llvm::StringRef file) {
|
||||
if(!path::is_absolute(file)) {
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
llvm::SmallString<128> path;
|
||||
|
||||
for(auto c: file) {
|
||||
if(c == '\\') {
|
||||
path.push_back('/');
|
||||
} else if(std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '/') {
|
||||
path.push_back(c);
|
||||
} else {
|
||||
path.push_back('%');
|
||||
path.push_back(llvm::hexdigit(c >> 4));
|
||||
path.push_back(llvm::hexdigit(c & 0xF));
|
||||
}
|
||||
}
|
||||
|
||||
return URI("file", "", path);
|
||||
}
|
||||
|
||||
llvm::Expected<URI> URI::parse(llvm::StringRef content) {
|
||||
URI result("", "", "");
|
||||
llvm::StringRef uri = content;
|
||||
auto pos = uri.find(':');
|
||||
if(pos == llvm::StringRef::npos) {
|
||||
return error("scheme is missing in URI: {}", content);
|
||||
} else {
|
||||
result.m_scheme = uri.substr(0, pos);
|
||||
if(!isValidScheme(result.m_scheme)) {
|
||||
return error("invalid scheme in URI: {}", content);
|
||||
}
|
||||
uri = uri.substr(pos + 1);
|
||||
}
|
||||
|
||||
if(uri.consume_front("//")) {
|
||||
pos = uri.find('/');
|
||||
result.m_authority = uri.substr(0, pos);
|
||||
uri = uri.substr(pos);
|
||||
}
|
||||
|
||||
result.m_body = decodePercent(uri);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string URI::resolve(llvm::StringRef content) {
|
||||
auto uri = parse(content);
|
||||
if(!uri) {
|
||||
std::terminate();
|
||||
}
|
||||
llvm::SmallString<128> result;
|
||||
if(auto err = fs::real_path(uri->body(), result)) {
|
||||
std::terminate();
|
||||
}
|
||||
return result.str().str();
|
||||
}
|
||||
|
||||
} // namespace clice
|
||||
@@ -1,5 +1,4 @@
|
||||
#include <Basic/URI.h>
|
||||
#include <Basic/SourceCode.h>
|
||||
#include <Basic/SourceConverter.h>
|
||||
#include <Compiler/Semantic.h>
|
||||
#include <Feature/CodeCompletion.h>
|
||||
|
||||
|
||||
@@ -12,41 +12,48 @@ struct FoldingRangeCollector : public clang::RecursiveASTVisitor<FoldingRangeCol
|
||||
|
||||
using Base = clang::RecursiveASTVisitor<FoldingRangeCollector>;
|
||||
|
||||
/// The converter used to adapt LSP protocol.
|
||||
const SourceConverter& cvtr;
|
||||
|
||||
/// The source manager of given AST.
|
||||
clang::SourceManager* src;
|
||||
clang::SourceManager& src;
|
||||
|
||||
/// Token buffer of given AST.
|
||||
clang::syntax::TokenBuffer* tkbuf;
|
||||
clang::syntax::TokenBuffer& tkbuf;
|
||||
|
||||
/// The result of folding ranges.
|
||||
proto::FoldingRangeResult result;
|
||||
|
||||
/// Do not produce folding ranges if either range ends is not within the main file.
|
||||
bool needFilter(clang::SourceLocation loc) {
|
||||
return loc.isInvalid() || !src->isInMainFile(loc);
|
||||
return loc.isInvalid() || !src.isInMainFile(loc);
|
||||
}
|
||||
|
||||
/// Get last column of previous line of a location.
|
||||
clang::SourceLocation prevLineLastColOf(clang::SourceLocation loc) {
|
||||
return src->translateLineCol(src->getMainFileID(),
|
||||
src->getPresumedLineNumber(loc) - 1,
|
||||
std::numeric_limits<unsigned>::max());
|
||||
return src.translateLineCol(src.getMainFileID(),
|
||||
src.getPresumedLineNumber(loc) - 1,
|
||||
std::numeric_limits<unsigned>::max());
|
||||
}
|
||||
|
||||
/// Collect source range as a folding range.
|
||||
void collect(const clang::SourceRange sr) {
|
||||
// - 1: convert 1-1 based LSP location to 0-0 based LSP location.
|
||||
proto::FoldingRange Range;
|
||||
Range.startLine = src->getPresumedLineNumber(sr.getBegin()) - 1;
|
||||
Range.endLine = src->getPresumedLineNumber(sr.getEnd()) - 1;
|
||||
void collect(const clang::SourceRange sr,
|
||||
proto::FoldingRangeKind kind = proto::FoldingRangeKind::Region) {
|
||||
auto startLine = src.getPresumedLineNumber(sr.getBegin()) - 1;
|
||||
auto endLine = src.getPresumedLineNumber(sr.getEnd()) - 1;
|
||||
|
||||
// Skip ranges on a single line.
|
||||
if(Range.startLine >= Range.endLine)
|
||||
if(startLine >= endLine)
|
||||
return;
|
||||
|
||||
Range.startCharacter = src->getPresumedColumnNumber(sr.getBegin()) - 1;
|
||||
Range.endCharacter = src->getPresumedColumnNumber(sr.getEnd()) - 1;
|
||||
result.push_back(Range);
|
||||
auto range = cvtr.toRange(sr, src);
|
||||
result.push_back({
|
||||
.startLine = range.start.line,
|
||||
.endLine = range.end.line,
|
||||
.startCharacter = range.start.character,
|
||||
.endCharacter = range.end.character,
|
||||
.kind = kind,
|
||||
});
|
||||
}
|
||||
|
||||
bool TraverseNamespaceDecl(clang::NamespaceDecl* decl) {
|
||||
@@ -57,7 +64,7 @@ struct FoldingRangeCollector : public clang::RecursiveASTVisitor<FoldingRangeCol
|
||||
}
|
||||
|
||||
bool VisitNamespaceDecl(const clang::NamespaceDecl* decl) {
|
||||
auto tks = tkbuf->expandedTokens(decl->getSourceRange());
|
||||
auto tks = tkbuf.expandedTokens(decl->getSourceRange());
|
||||
|
||||
// Find first '{' in namespace declaration.
|
||||
auto shrink = tks.drop_until([](const clang::syntax::Token& tk) -> bool {
|
||||
@@ -70,7 +77,7 @@ struct FoldingRangeCollector : public clang::RecursiveASTVisitor<FoldingRangeCol
|
||||
|
||||
/// Collect lambda capture list "[ ... ]".
|
||||
void collectLambdaCapture(const clang::CXXRecordDecl* decl) {
|
||||
auto tks = tkbuf->expandedTokens(decl->getSourceRange());
|
||||
auto tks = tkbuf.expandedTokens(decl->getSourceRange());
|
||||
|
||||
auto shrink = tks.drop_until([](const clang::syntax::Token& tk) -> bool {
|
||||
return tk.kind() == clang::tok::TokenKind::l_square;
|
||||
@@ -110,20 +117,19 @@ struct FoldingRangeCollector : public clang::RecursiveASTVisitor<FoldingRangeCol
|
||||
}
|
||||
};
|
||||
|
||||
auto tks = tkbuf->expandedTokens(decl->getSourceRange());
|
||||
auto tks = tkbuf.expandedTokens(decl->getSourceRange());
|
||||
|
||||
auto tryCollectRegion = [this](clang::SourceLocation ll, clang::SourceLocation lr) {
|
||||
// Skip continous access control keywords.
|
||||
if(src->getPresumedLineNumber(ll) == src->getPresumedLineNumber(lr))
|
||||
if(src.getPresumedLineNumber(ll) == src.getPresumedLineNumber(lr))
|
||||
return;
|
||||
collect({ll, prevLineLastColOf(lr)});
|
||||
};
|
||||
|
||||
// If there is no access control blocks, return.
|
||||
tks = tks.drop_until(is_accctrl);
|
||||
if(tks.empty()) {
|
||||
if(tks.empty())
|
||||
return;
|
||||
}
|
||||
|
||||
auto [_, rb] = decl->getBraceRange();
|
||||
tks = tks.drop_front(); // Move to ':' after private/public/protected
|
||||
@@ -150,6 +156,7 @@ struct FoldingRangeCollector : public clang::RecursiveASTVisitor<FoldingRangeCol
|
||||
|
||||
bool VisitTagDecl(const clang::TagDecl* decl) {
|
||||
auto [lb, rb] = decl->getBraceRange();
|
||||
auto name = decl->getName();
|
||||
collect({lb.getLocWithOffset(1), prevLineLastColOf(rb)});
|
||||
|
||||
if(auto cxd = llvm::dyn_cast<clang::CXXRecordDecl>(decl);
|
||||
@@ -161,7 +168,7 @@ struct FoldingRangeCollector : public clang::RecursiveASTVisitor<FoldingRangeCol
|
||||
|
||||
/// Collect function parameter list between '(' and ')'.
|
||||
void collectParameterList(clang::SourceLocation left, clang::SourceLocation right) {
|
||||
auto tks = tkbuf->expandedTokens({left, right});
|
||||
auto tks = tkbuf.expandedTokens({left, right});
|
||||
|
||||
tks = tks.drop_until([](const auto& tk) { return tk.kind() == clang::tok::l_paren; });
|
||||
if(tks.empty())
|
||||
@@ -239,7 +246,7 @@ struct FoldingRangeCollector : public clang::RecursiveASTVisitor<FoldingRangeCol
|
||||
}
|
||||
|
||||
bool VisitCallExpr(const clang::CallExpr* expr) {
|
||||
auto tks = tkbuf->expandedTokens(expr->getSourceRange());
|
||||
auto tks = tkbuf.expandedTokens(expr->getSourceRange());
|
||||
if(tks.back().kind() != clang::tok::r_paren)
|
||||
return true;
|
||||
|
||||
@@ -291,7 +298,7 @@ struct FoldingRangeCollector : public clang::RecursiveASTVisitor<FoldingRangeCol
|
||||
|
||||
void collectDrectives(const ASTDirectives& direcs) {
|
||||
for(auto& [fileid, dirc]: direcs) {
|
||||
if(fileid != src->getMainFileID())
|
||||
if(fileid != src.getMainFileID())
|
||||
continue;
|
||||
|
||||
collectConditionMacro(dirc.conditions);
|
||||
@@ -347,16 +354,23 @@ struct FoldingRangeCollector : public clang::RecursiveASTVisitor<FoldingRangeCol
|
||||
|
||||
namespace feature {
|
||||
|
||||
proto::FoldingRangeResult foldingRange(FoldingRangeParams& _, ASTInfo& ast) {
|
||||
json::Value foldingRangeCapability(json::Value foldingRangeClientCapabilities) {
|
||||
// Always return empty object.
|
||||
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_foldingRange
|
||||
return {};
|
||||
}
|
||||
|
||||
proto::FoldingRangeResult foldingRange(FoldingRangeParams& _, ASTInfo& info,
|
||||
const SourceConverter& converter) {
|
||||
|
||||
FoldingRangeCollector collector{
|
||||
.src = &ast.srcMgr(),
|
||||
.tkbuf = &ast.tokBuf(),
|
||||
.result = {},
|
||||
.cvtr = converter,
|
||||
.src = info.srcMgr(),
|
||||
.tkbuf = info.tokBuf(),
|
||||
};
|
||||
|
||||
collector.collectDrectives(ast.directives());
|
||||
collector.TraverseTranslationUnitDecl(ast.tu());
|
||||
collector.collectDrectives(info.directives());
|
||||
collector.TraverseTranslationUnitDecl(info.tu());
|
||||
|
||||
return std::move(collector.result);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include <Basic/SourceCode.h>
|
||||
#include <Compiler/Compiler.h>
|
||||
#include <Feature/SignatureHelp.h>
|
||||
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
#include "Basic/SourceConverter.h"
|
||||
#include "Server/Server.h"
|
||||
|
||||
namespace clice {
|
||||
|
||||
async::promise<void> Server::onDidOpen(const proto::DidOpenTextDocumentParams& params) {
|
||||
auto path = URI::resolve(params.textDocument.uri);
|
||||
auto path = SourceConverter::toPath(params.textDocument.uri);
|
||||
llvm::StringRef content = params.textDocument.text;
|
||||
co_await scheduler.update(path, content, synchronizer);
|
||||
}
|
||||
|
||||
async::promise<void> Server::onDidChange(const proto::DidChangeTextDocumentParams& document) {
|
||||
auto path = URI::resolve(document.textDocument.uri);
|
||||
auto path = SourceConverter::toPath(document.textDocument.uri);
|
||||
llvm::StringRef content = document.contentChanges[0].text;
|
||||
co_await scheduler.update(path, content, synchronizer);
|
||||
}
|
||||
|
||||
async::promise<void> Server::onDidSave(const proto::DidSaveTextDocumentParams& document) {
|
||||
auto path = URI::resolve(document.textDocument.uri);
|
||||
auto path = SourceConverter::toPath(document.textDocument.uri);
|
||||
/// co_await scheduler.save(path);
|
||||
co_return;
|
||||
}
|
||||
|
||||
async::promise<void> Server::onDidClose(const proto::DidCloseTextDocumentParams& document) {
|
||||
auto path = URI::resolve(document.textDocument.uri);
|
||||
auto path = SourceConverter::toPath(document.textDocument.uri);
|
||||
/// co_await scheduler.close(path);
|
||||
co_return;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "Basic/SourceConverter.h"
|
||||
#include "Server/Server.h"
|
||||
|
||||
namespace clice {
|
||||
@@ -27,9 +28,8 @@ async::promise<void> Server::onFindReferences(json::Value id,
|
||||
co_return;
|
||||
}
|
||||
|
||||
async::promise<void>
|
||||
Server::onPrepareCallHierarchy(json::Value id,
|
||||
const proto::CallHierarchyPrepareParams& params) {
|
||||
async::promise<void> Server::onPrepareCallHierarchy(
|
||||
json::Value id, const proto::CallHierarchyPrepareParams& params) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
@@ -43,9 +43,8 @@ async::promise<void> Server::onOutgoingCall(json::Value id,
|
||||
co_return;
|
||||
}
|
||||
|
||||
async::promise<void>
|
||||
Server::onPrepareTypeHierarchy(json::Value id,
|
||||
const proto::TypeHierarchyPrepareParams& params) {
|
||||
async::promise<void> Server::onPrepareTypeHierarchy(
|
||||
json::Value id, const proto::TypeHierarchyPrepareParams& params) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
@@ -89,7 +88,7 @@ async::promise<void> Server::onDocumentSymbol(json::Value id,
|
||||
|
||||
async::promise<void> Server::onSemanticTokens(json::Value id,
|
||||
const proto::SemanticTokensParams& params) {
|
||||
auto path = URI::resolve(params.textDocument.uri);
|
||||
auto path = SourceConverter::toPath(params.textDocument.uri);
|
||||
proto::SemanticTokens result;
|
||||
co_await scheduler.execute(path, [&id, &path, &result](ASTInfo& info) {
|
||||
result = feature::semanticTokens(info, path);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#include "Basic/SourceConverter.h"
|
||||
#include "Server/Server.h"
|
||||
|
||||
namespace clice {
|
||||
|
||||
async::promise<void> Server::onInitialize(json::Value id, const proto::InitializeParams& params) {
|
||||
auto workplace = URI::resolve(params.workspaceFolders[0].uri);
|
||||
auto workplace = SourceConverter::toPath(params.workspaceFolders[0].uri);
|
||||
config::init(workplace);
|
||||
|
||||
if(!params.capabilities.workspace.didChangeWatchedFiles.dynamicRegistration) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "Basic/SourceConverter.h"
|
||||
#include "Server/Server.h"
|
||||
|
||||
namespace clice {
|
||||
@@ -10,7 +11,7 @@ async::promise<> Server::onDidChangeWatchedFiles(const proto::DidChangeWatchedFi
|
||||
}
|
||||
|
||||
case proto::FileChangeType::Changed: {
|
||||
auto path = URI::resolve(event.uri);
|
||||
auto path = SourceConverter::toPath(event.uri);
|
||||
synchronizer.sync(path);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
#include "../Test.h"
|
||||
#include "Basic/SourceCode.h"
|
||||
|
||||
namespace clice {
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(SourceCode, Remeasure) {
|
||||
EXPECT_EQ(remeasure("", proto::PositionEncodingKind::UTF8), 0);
|
||||
EXPECT_EQ(remeasure("ascii", proto::PositionEncodingKind::UTF8), 5);
|
||||
|
||||
EXPECT_EQ(remeasure("↓", proto::PositionEncodingKind::UTF16), 1);
|
||||
EXPECT_EQ(remeasure("¥", proto::PositionEncodingKind::UTF16), 1);
|
||||
|
||||
EXPECT_EQ(remeasure("😂", proto::PositionEncodingKind::UTF16), 2);
|
||||
EXPECT_EQ(remeasure("😂", proto::PositionEncodingKind::UTF32), 1);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace clice
|
||||
|
||||
93
unittests/Basic/SourceConverter.cpp
Normal file
93
unittests/Basic/SourceConverter.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
#include "../Test.h"
|
||||
#include "Basic/SourceConverter.h"
|
||||
|
||||
namespace clice {
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(SourceConverter, Remeasure) {
|
||||
using SC = SourceConverter;
|
||||
|
||||
SourceConverter utf8{proto::PositionEncodingKind::UTF8};
|
||||
|
||||
EXPECT_EQ(utf8.remeasure(""), 0);
|
||||
EXPECT_EQ(utf8.remeasure("ascii"), 5);
|
||||
|
||||
SourceConverter utf16{proto::PositionEncodingKind::UTF16};
|
||||
|
||||
EXPECT_EQ(utf16.remeasure("↓"), 1);
|
||||
EXPECT_EQ(utf16.remeasure("¥"), 1);
|
||||
|
||||
SourceConverter utf32{proto::PositionEncodingKind::UTF32};
|
||||
EXPECT_EQ(utf8.remeasure("😂"), 4);
|
||||
EXPECT_EQ(utf16.remeasure("😂"), 2);
|
||||
EXPECT_EQ(utf32.remeasure("😂"), 1);
|
||||
}
|
||||
|
||||
TEST(SourceConverter, Position) {
|
||||
const char* main = "int a /*😂*/ = 1;$(eof)";
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run("-std=c++11");
|
||||
|
||||
auto& src = txs.info.srcMgr();
|
||||
auto& tks = txs.info.tokBuf();
|
||||
|
||||
auto mainid = src.getMainFileID();
|
||||
auto tokens =
|
||||
tks.expandedTokens({src.getLocForStartOfFile(mainid), src.getLocForEndOfFile(mainid)});
|
||||
|
||||
auto eof = tokens.back().endLocation();
|
||||
txs.expect("eof", eof);
|
||||
|
||||
{
|
||||
SourceConverter cvtr{proto::PositionEncodingKind::UTF8};
|
||||
auto pos = cvtr.toPosition(eof, src);
|
||||
EXPECT_EQ(pos.line, 0);
|
||||
EXPECT_EQ(pos.character, 19);
|
||||
}
|
||||
|
||||
{
|
||||
SourceConverter cvtr{proto::PositionEncodingKind::UTF16};
|
||||
auto pos = cvtr.toPosition(eof, src);
|
||||
EXPECT_EQ(pos.line, 0);
|
||||
EXPECT_EQ(pos.character, 19);
|
||||
}
|
||||
|
||||
{
|
||||
SourceConverter cvtr{proto::PositionEncodingKind::UTF32};
|
||||
auto pos = cvtr.toPosition(eof, src);
|
||||
EXPECT_EQ(pos.line, 0);
|
||||
EXPECT_EQ(pos.character, 19);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SourceConverter, UriAndFsPath) {
|
||||
using SC = SourceConverter;
|
||||
|
||||
// It must be a existed file.
|
||||
#ifdef unix
|
||||
const char* fspath[] = {"/dev/null"};
|
||||
const char* uri[] = {"file:///dev/null"};
|
||||
#else
|
||||
const char* fspath[] = {};
|
||||
const char* uri[] = {};
|
||||
#endif
|
||||
EXPECT_EQ(std::size(fspath), std::size(uri));
|
||||
|
||||
for(int i = 0; i < std::size(fspath); ++i) {
|
||||
auto fspath_ = fspath[i];
|
||||
auto uri_ = uri[i];
|
||||
|
||||
auto uri1 = SC::toURI(fspath_);
|
||||
EXPECT_EQ(uri1, uri_);
|
||||
|
||||
auto fspath2 = SC::toPath(uri_);
|
||||
EXPECT_EQ(fspath2, fspath_);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace clice
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <Basic/URI.h>
|
||||
|
||||
namespace clice {
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(URI, Basic) {
|
||||
URI uri("https", "reviews.llvm.org", "/D41946");
|
||||
|
||||
EXPECT_EQ(uri.scheme(), "https");
|
||||
EXPECT_EQ(uri.authority(), "reviews.llvm.org");
|
||||
EXPECT_EQ(uri.body(), "/D41946");
|
||||
}
|
||||
|
||||
TEST(URI, Copy) {
|
||||
URI uri1("https", "reviews.llvm.org", "/D41946");
|
||||
URI uri2(uri1);
|
||||
|
||||
EXPECT_EQ(uri2.scheme(), "https");
|
||||
EXPECT_EQ(uri2.authority(), "reviews.llvm.org");
|
||||
EXPECT_EQ(uri2.body(), "/D41946");
|
||||
}
|
||||
|
||||
TEST(URI, Eq) {
|
||||
URI uri1("https", "reviews.llvm.org", "/D41946");
|
||||
URI uri2("https", "reviews.llvm.org", "/D41946");
|
||||
URI uri3("http", "example.com", "/index.html");
|
||||
|
||||
EXPECT_TRUE(uri1 == uri2);
|
||||
EXPECT_FALSE(uri1 == uri3);
|
||||
}
|
||||
|
||||
TEST(URI, File) {
|
||||
auto uri = URI::from("/home/user/file.txt");
|
||||
EXPECT_EQ(uri.scheme(), "file");
|
||||
EXPECT_EQ(uri.authority(), "");
|
||||
EXPECT_EQ(uri.body(), "/home/user/file.txt");
|
||||
EXPECT_EQ(uri.toString(), "file:///home/user/file.txt");
|
||||
}
|
||||
|
||||
TEST(URI, Parse) {
|
||||
auto expectedUri = URI::parse("https://reviews.llvm.org/D41946");
|
||||
ASSERT_TRUE(bool(expectedUri));
|
||||
|
||||
URI& uri = expectedUri.get();
|
||||
EXPECT_EQ(uri.scheme(), "https");
|
||||
EXPECT_EQ(uri.authority(), "reviews.llvm.org");
|
||||
EXPECT_EQ(uri.body(), "/D41946");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace clice
|
||||
|
||||
@@ -56,8 +56,9 @@ namespace ugly
|
||||
|
||||
auto& info = txs.info;
|
||||
|
||||
SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
FoldingRangeParams param;
|
||||
auto res = feature::foldingRange(param, info);
|
||||
auto res = feature::foldingRange(param, info, converter);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
@@ -105,8 +106,9 @@ enum class _2 {$(3)
|
||||
return fromLspLocation(src, fr);
|
||||
};
|
||||
|
||||
SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
FoldingRangeParams param;
|
||||
auto res = feature::foldingRange(param, info);
|
||||
auto res = feature::foldingRange(param, info, converter);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
@@ -161,8 +163,9 @@ void f() {$(9)
|
||||
return fromLspLocation(src, fr);
|
||||
};
|
||||
|
||||
SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
FoldingRangeParams param;
|
||||
auto res = feature::foldingRange(param, info);
|
||||
auto res = feature::foldingRange(param, info, converter);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
@@ -222,8 +225,9 @@ struct _4;
|
||||
return fromLspLocation(src, fr);
|
||||
};
|
||||
|
||||
SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
FoldingRangeParams param;
|
||||
auto res = feature::foldingRange(param, info);
|
||||
auto res = feature::foldingRange(param, info, converter);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
@@ -270,8 +274,9 @@ auto s = [$(5)
|
||||
return fromLspLocation(src, fr);
|
||||
};
|
||||
|
||||
SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
FoldingRangeParams param;
|
||||
auto res = feature::foldingRange(param, info);
|
||||
auto res = feature::foldingRange(param, info, converter);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
@@ -324,8 +329,9 @@ auto _3 = []($(5)
|
||||
return fromLspLocation(src, fr);
|
||||
};
|
||||
|
||||
SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
FoldingRangeParams param;
|
||||
auto res = feature::foldingRange(param, info);
|
||||
auto res = feature::foldingRange(param, info, converter);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
@@ -379,8 +385,9 @@ void d($(5)
|
||||
return fromLspLocation(src, fr);
|
||||
};
|
||||
|
||||
SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
FoldingRangeParams param;
|
||||
auto res = feature::foldingRange(param, info);
|
||||
auto res = feature::foldingRange(param, info, converter);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
@@ -428,8 +435,9 @@ void n() {$(5)
|
||||
return fromLspLocation(src, fr);
|
||||
};
|
||||
|
||||
SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
FoldingRangeParams param;
|
||||
auto res = feature::foldingRange(param, info);
|
||||
auto res = feature::foldingRange(param, info, converter);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
@@ -474,8 +482,9 @@ int main() {$(1)
|
||||
return fromLspLocation(src, fr);
|
||||
};
|
||||
|
||||
SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
FoldingRangeParams param;
|
||||
auto res = feature::foldingRange(param, info);
|
||||
auto res = feature::foldingRange(param, info, converter);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
@@ -526,8 +535,9 @@ int main () {$(1)
|
||||
return fromLspLocation(src, fr);
|
||||
};
|
||||
|
||||
SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
FoldingRangeParams param;
|
||||
auto res = feature::foldingRange(param, info);
|
||||
auto res = feature::foldingRange(param, info, converter);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
@@ -575,8 +585,9 @@ L l2 = {$(3)
|
||||
return fromLspLocation(src, fr);
|
||||
};
|
||||
|
||||
SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
FoldingRangeParams param;
|
||||
auto res = feature::foldingRange(param, info);
|
||||
auto res = feature::foldingRange(param, info, converter);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
@@ -628,8 +639,9 @@ public:$(12)
|
||||
return fromLspLocation(src, fr);
|
||||
};
|
||||
|
||||
SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
FoldingRangeParams param;
|
||||
auto res = feature::foldingRange(param, info);
|
||||
auto res = feature::foldingRange(param, info, converter);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
@@ -679,8 +691,9 @@ $(2)
|
||||
return fromLspLocation(src, fr);
|
||||
};
|
||||
|
||||
SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
FoldingRangeParams param;
|
||||
auto res = feature::foldingRange(param, info);
|
||||
auto res = feature::foldingRange(param, info, converter);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "../Test.h"
|
||||
#include "Index/SymbolIndex.h"
|
||||
#include "Basic/SourceCode.h"
|
||||
#include "Basic/SourceConverter.h"
|
||||
|
||||
namespace clice {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user