summaryrefslogtreecommitdiff
path: root/clang-tools-extra/clangd/ClangdUnit.cpp
diff options
context:
space:
mode:
authorSam McCall <sam.mccall@gmail.com>2018-04-27 11:59:28 +0000
committerSam McCall <sam.mccall@gmail.com>2018-04-27 11:59:28 +0000
commita58548fac8cbe6c59e72b9822110b06011f283b2 (patch)
tree4583b5b138fc59dbbca3a8b3c98109ff3b81b035 /clang-tools-extra/clangd/ClangdUnit.cpp
parentb7305d7af9f426231c36f3ba527f932b05ad254e (diff)
[clangd] Fix unicode handling, using UTF-16 where LSP requires it.
Summary: The Language Server Protocol unfortunately mandates that locations in files be represented by line/column pairs, where the "column" is actually an index into the UTF-16-encoded text of the line. (This is because VSCode is written in JavaScript, which is UTF-16-native). Internally clangd treats source files at UTF-8, the One True Encoding, and generally deals with byte offsets (though there are exceptions). Before this patch, conversions between offsets and LSP Position pretended that Position.character was UTF-8 bytes, which is only true for ASCII lines. Now we examine the text to convert correctly (but don't actually need to transcode it, due to some nice details of the encodings). The updated functions in SourceCode are the blessed way to interact with the Position.character field, and anything else is likely to be wrong. So I also updated the other accesses: - CodeComplete needs a "clang-style" line/column, with column in utf-8 bytes. This is now converted via Position -> offset -> clang line/column (a new function is added to SourceCode.h for the second conversion). - getBeginningOfIdentifier skipped backwards in UTF-16 space, which is will behave badly when it splits a surrogate pair. Skipping backwards in UTF-8 coordinates gives the lexer a fighting chance of getting this right. While here, I clarified(?) the logic comments, fixed a bug with identifiers containing digits, simplified the signature slightly and added a test. This seems likely to cause problems with editors that have the same bug, and treat the protocol as if columns are UTF-8 bytes. But we can find and fix those. Reviewers: hokein Subscribers: klimek, ilya-biryukov, ioeric, MaskRay, jkorous, cfe-commits Differential Revision: https://reviews.llvm.org/D46035
Diffstat (limited to 'clang-tools-extra/clangd/ClangdUnit.cpp')
-rw-r--r--clang-tools-extra/clangd/ClangdUnit.cpp73
1 files changed, 27 insertions, 46 deletions
diff --git a/clang-tools-extra/clangd/ClangdUnit.cpp b/clang-tools-extra/clangd/ClangdUnit.cpp
index 7309e1ad49b..c750f578e65 100644
--- a/clang-tools-extra/clangd/ClangdUnit.cpp
+++ b/clang-tools-extra/clangd/ClangdUnit.cpp
@@ -215,19 +215,6 @@ ParsedAST::Build(std::unique_ptr<clang::CompilerInvocation> CI,
std::move(IncLocations));
}
-namespace {
-
-SourceLocation getMacroArgExpandedLocation(const SourceManager &Mgr,
- const FileEntry *FE, Position Pos) {
- // The language server protocol uses zero-based line and column numbers.
- // Clang uses one-based numbers.
- SourceLocation InputLoc =
- Mgr.translateFileLineCol(FE, Pos.line + 1, Pos.character + 1);
- return Mgr.getMacroArgExpandedLocation(InputLoc);
-}
-
-} // namespace
-
void ParsedAST::ensurePreambleDeclsDeserialized() {
if (PreambleDeclsDeserialized || !Preamble)
return;
@@ -470,40 +457,34 @@ CppFile::rebuildPreamble(CompilerInvocation &CI,
SourceLocation clangd::getBeginningOfIdentifier(ParsedAST &Unit,
const Position &Pos,
- const FileEntry *FE) {
+ const FileID FID) {
const ASTContext &AST = Unit.getASTContext();
const SourceManager &SourceMgr = AST.getSourceManager();
-
- SourceLocation InputLocation =
- getMacroArgExpandedLocation(SourceMgr, FE, Pos);
- if (Pos.character == 0) {
- return InputLocation;
- }
-
- // This handle cases where the position is in the middle of a token or right
- // after the end of a token. In theory we could just use GetBeginningOfToken
- // to find the start of the token at the input position, but this doesn't
- // work when right after the end, i.e. foo|.
- // So try to go back by one and see if we're still inside an identifier
- // token. If so, Take the beginning of this token.
- // (It should be the same identifier because you can't have two adjacent
- // identifiers without another token in between.)
- Position PosCharBehind = Pos;
- --PosCharBehind.character;
-
- SourceLocation PeekBeforeLocation =
- getMacroArgExpandedLocation(SourceMgr, FE, PosCharBehind);
- Token Result;
- if (Lexer::getRawToken(PeekBeforeLocation, Result, SourceMgr,
- AST.getLangOpts(), false)) {
- // getRawToken failed, just use InputLocation.
- return InputLocation;
+ auto Offset = positionToOffset(SourceMgr.getBufferData(FID), Pos);
+ if (!Offset) {
+ log("getBeginningOfIdentifier: " + toString(Offset.takeError()));
+ return SourceLocation();
}
-
- if (Result.is(tok::raw_identifier)) {
- return Lexer::GetBeginningOfToken(PeekBeforeLocation, SourceMgr,
- AST.getLangOpts());
- }
-
- return InputLocation;
+ SourceLocation InputLoc = SourceMgr.getComposedLoc(FID, *Offset);
+
+ // GetBeginningOfToken(pos) is almost what we want, but does the wrong thing
+ // if the cursor is at the end of the identifier.
+ // Instead, we lex at GetBeginningOfToken(pos - 1). The cases are:
+ // 1) at the beginning of an identifier, we'll be looking at something
+ // that isn't an identifier.
+ // 2) at the middle or end of an identifier, we get the identifier.
+ // 3) anywhere outside an identifier, we'll get some non-identifier thing.
+ // We can't actually distinguish cases 1 and 3, but returning the original
+ // location is correct for both!
+ if (*Offset == 0) // Case 1 or 3.
+ return SourceMgr.getMacroArgExpandedLocation(InputLoc);
+ SourceLocation Before =
+ SourceMgr.getMacroArgExpandedLocation(InputLoc.getLocWithOffset(-1));
+ Before = Lexer::GetBeginningOfToken(Before, SourceMgr, AST.getLangOpts());
+ Token Tok;
+ if (Before.isValid() &&
+ !Lexer::getRawToken(Before, Tok, SourceMgr, AST.getLangOpts(), false) &&
+ Tok.is(tok::raw_identifier))
+ return Before; // Case 2.
+ return SourceMgr.getMacroArgExpandedLocation(InputLoc); // Case 1 or 3.
}