llvm-project/mlir/lib/Tools/lsp-server-support/SourceMgrUtils.cpp

159 lines
5.4 KiB
C++

//===--- SourceMgrUtils.cpp - SourceMgr LSP Utils -------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "SourceMgrUtils.h"
#include "llvm/Support/Path.h"
using namespace mlir;
using namespace mlir::lsp;
//===----------------------------------------------------------------------===//
// Utils
//===----------------------------------------------------------------------===//
/// Find the end of a string whose contents start at the given `curPtr`. Returns
/// the position at the end of the string, after a terminal or invalid character
/// (e.g. `"` or `\0`).
static const char *lexLocStringTok(const char *curPtr) {
while (char c = *curPtr++) {
// Check for various terminal characters.
if (StringRef("\"\n\v\f").contains(c))
return curPtr;
// Check for escape sequences.
if (c == '\\') {
// Check a few known escapes and \xx hex digits.
if (*curPtr == '"' || *curPtr == '\\' || *curPtr == 'n' || *curPtr == 't')
++curPtr;
else if (llvm::isHexDigit(*curPtr) && llvm::isHexDigit(curPtr[1]))
curPtr += 2;
else
return curPtr;
}
}
// If we hit this point, we've reached the end of the buffer. Update the end
// pointer to not point past the buffer.
return curPtr - 1;
}
SMRange lsp::convertTokenLocToRange(SMLoc loc) {
if (!loc.isValid())
return SMRange();
const char *curPtr = loc.getPointer();
// Check if this is a string token.
if (*curPtr == '"') {
curPtr = lexLocStringTok(curPtr + 1);
// Otherwise, default to handling an identifier.
} else {
// Return if the given character is a valid identifier character.
auto isIdentifierChar = [](char c) {
return isalnum(c) || c == '$' || c == '.' || c == '_' || c == '-';
};
while (*curPtr && isIdentifierChar(*(++curPtr)))
continue;
}
return SMRange(loc, SMLoc::getFromPointer(curPtr));
}
Optional<std::string> lsp::extractSourceDocComment(llvm::SourceMgr &sourceMgr,
SMLoc loc) {
// This is a heuristic, and isn't intended to cover every case, but should
// cover the most common. We essentially look for a comment preceding the
// line, and if we find one, use that as the documentation.
if (!loc.isValid())
return std::nullopt;
int bufferId = sourceMgr.FindBufferContainingLoc(loc);
if (bufferId == 0)
return std::nullopt;
const char *bufferStart =
sourceMgr.getMemoryBuffer(bufferId)->getBufferStart();
StringRef buffer(bufferStart, loc.getPointer() - bufferStart);
// Pop the last line from the buffer string.
auto popLastLine = [&]() -> Optional<StringRef> {
size_t newlineOffset = buffer.find_last_of("\n");
if (newlineOffset == StringRef::npos)
return std::nullopt;
StringRef lastLine = buffer.drop_front(newlineOffset).trim();
buffer = buffer.take_front(newlineOffset);
return lastLine;
};
// Try to pop the current line.
if (!popLastLine())
return std::nullopt;
// Try to parse a comment string from the source file.
SmallVector<StringRef> commentLines;
while (Optional<StringRef> line = popLastLine()) {
// Check for a comment at the beginning of the line.
if (!line->startswith("//"))
break;
// Extract the document string from the comment.
commentLines.push_back(line->drop_while([](char c) { return c == '/'; }));
}
if (commentLines.empty())
return std::nullopt;
return llvm::join(llvm::reverse(commentLines), "\n");
}
bool lsp::contains(SMRange range, SMLoc loc) {
return range.Start.getPointer() <= loc.getPointer() &&
loc.getPointer() < range.End.getPointer();
}
//===----------------------------------------------------------------------===//
// SourceMgrInclude
//===----------------------------------------------------------------------===//
Hover SourceMgrInclude::buildHover() const {
Hover hover(range);
{
llvm::raw_string_ostream hoverOS(hover.contents.value);
hoverOS << "`" << llvm::sys::path::filename(uri.file()) << "`\n***\n"
<< uri.file();
}
return hover;
}
void lsp::gatherIncludeFiles(llvm::SourceMgr &sourceMgr,
SmallVectorImpl<SourceMgrInclude> &includes) {
for (unsigned i = 1, e = sourceMgr.getNumBuffers(); i < e; ++i) {
// Check to see if this file was included by the main file.
SMLoc includeLoc = sourceMgr.getBufferInfo(i + 1).IncludeLoc;
if (!includeLoc.isValid() || sourceMgr.FindBufferContainingLoc(
includeLoc) != sourceMgr.getMainFileID())
continue;
// Try to build a URI for this file path.
auto *buffer = sourceMgr.getMemoryBuffer(i + 1);
llvm::SmallString<256> path(buffer->getBufferIdentifier());
llvm::sys::path::remove_dots(path, /*remove_dot_dot=*/true);
llvm::Expected<URIForFile> includedFileURI = URIForFile::fromFile(path);
if (!includedFileURI)
continue;
// Find the end of the include token.
const char *includeStart = includeLoc.getPointer() - 2;
while (*(--includeStart) != '\"')
continue;
// Push this include.
SMRange includeRange(SMLoc::getFromPointer(includeStart), includeLoc);
includes.emplace_back(*includedFileURI, Range(sourceMgr, includeRange));
}
}