Implement SourceConverter (#26)

This commit is contained in:
Shiyu
2025-01-10 19:26:41 +08:00
committed by GitHub
parent b7d58d03f4
commit c1c1930757
20 changed files with 343 additions and 348 deletions

View File

@@ -21,7 +21,4 @@ using array = std::vector<T>;
using DocumentUri = std::string;
// TODO: figure out URI.
using URI = std::string;
} // namespace clice::proto

View File

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

View 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

View File

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

View File

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

View File

@@ -1,7 +1,5 @@
#pragma once
#include "Basic/URI.h"
#include "Feature/Lookup.h"
#include "Feature/DocumentHighlight.h"
#include "Feature/DocumentLink.h"

View File

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

View File

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

View File

@@ -1,5 +1,4 @@
#include <Basic/URI.h>
#include <Basic/SourceCode.h>
#include <Basic/SourceConverter.h>
#include <Compiler/Semantic.h>
#include <Feature/CodeCompletion.h>

View File

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

View File

@@ -1,4 +1,3 @@
#include <Basic/SourceCode.h>
#include <Compiler/Compiler.h>
#include <Feature/SignatureHelp.h>

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
#include "../Test.h"
#include "Index/SymbolIndex.h"
#include "Basic/SourceCode.h"
#include "Basic/SourceConverter.h"
namespace clice {