35#include "slang/syntax/AllSyntax.h"
36#include "slang/syntax/SyntaxTree.h"
38#include "mlir/IR/Attributes.h"
39#include "mlir/IR/BuiltinAttributes.h"
40#include "mlir/IR/Location.h"
41#include "mlir/Support/FileUtilities.h"
43#include "llvm/Support/ConvertUTF.h"
44#include "llvm/Support/FileSystem.h"
45#include "llvm/Support/LineIterator.h"
46#include "llvm/Support/Path.h"
55using namespace llvm::lsp;
59 if (!driver.parseAllSources()) {
64 std::vector<std::string> topModules;
65 for (
auto &t : driver.syntaxTrees) {
67 t->root().as_if<slang::syntax::CompilationUnitSyntax>()) {
68 for (
auto *member : compUnit->members) {
71 if (
auto *moduleDecl =
72 member->as_if<slang::syntax::ModuleDeclarationSyntax>()) {
73 topModules.emplace_back(moduleDecl->header->name.valueText());
78 driver.options.topModules = std::move(topModules);
83 const slang::driver::Driver *
const projectDriver,
84 const llvm::SmallString<256> &mainBufferFileName) {
85 for (
auto bId : projectDriver->sourceManager.getAllBuffers()) {
86 std::string_view slangRawPath =
87 projectDriver->sourceManager.getRawFileName(bId);
89 llvm::SmallString<256> slangCanonPath;
90 if (llvm::sys::fs::real_path(slangRawPath, slangCanonPath))
97 bool alreadyLoaded =
false;
98 for (
auto id : driver.sourceManager.getAllBuffers()) {
99 if (driver.sourceManager.getFullPath(
id).string() ==
100 slangCanonPath.str()) {
101 alreadyLoaded =
true;
108 auto buffer = driver.sourceManager.assignText(
109 slangCanonPath.str(), projectDriver->sourceManager.getSourceText(bId));
110 driver.sourceLoader.addBuffer(buffer);
116 StringRef contents, std::vector<llvm::lsp::Diagnostic> &diagnostics,
117 const slang::driver::Driver *
const projectDriver,
118 const std::vector<std::string> &projectIncludeDirectories)
119 : globalContext(
context), uri(uri) {
121 llvm::SmallString<256> canonPath(uri.file());
122 if (std::error_code ec = llvm::sys::fs::real_path(uri.file(), canonPath))
123 canonPath = uri.file();
126 llvm::SmallString<32> uriDirectory(uri.file());
127 llvm::sys::path::remove_filename(uriDirectory);
129 std::vector<std::string> libDirs;
130 libDirs.push_back(uriDirectory.str().str());
131 libDirs.insert(libDirs.end(),
context.options.libDirs.begin(),
132 context.options.libDirs.end());
134 for (
const auto &libDir : libDirs)
135 driver.sourceLoader.addSearchDirectories(libDir);
137 auto memBuffer = llvm::MemoryBuffer::getMemBufferCopy(contents, uri.file());
140 Twine(
"Failed to create memory buffer for file ") + uri.file());
144 auto topSlangBuffer =
145 driver.sourceManager.assignText(uri.file(), memBuffer->getBuffer());
146 driver.sourceLoader.addBuffer(topSlangBuffer);
147 mainBufferId = topSlangBuffer.id;
149 auto diagClient = std::make_shared<LSPDiagnosticClient>(*
this, diagnostics);
150 driver.diagEngine.addClient(diagClient);
152 driver.options.compilationFlags.emplace(
153 slang::ast::CompilationFlags::LintMode,
false);
154 driver.options.compilationFlags.emplace(
155 slang::ast::CompilationFlags::DisableInstanceCaching,
false);
157 for (
auto &dir : projectIncludeDirectories)
158 (void)driver.sourceManager.addUserDirectories(dir);
160 if (!driver.processOptions()) {
165 driver.diagEngine.setIgnoreAllWarnings(
false);
170 driver.options = projectDriver->options;
177 if (!driver.parseAllSources()) {
183 compilation = driver.createCompilation();
184 if (failed(compilation)) {
190 for (
auto &diag : (*compilation)->getAllDiagnostics())
191 driver.diagEngine.issue(diag);
193 computeLineOffsets(driver.sourceManager.getSourceText(mainBufferId));
195 index = std::make_unique<VerilogIndex>(mainBufferId, driver.sourceManager);
197 index->initialize(**compilation);
202 if (loc && loc.buffer() != slang::SourceLocation::NoLocation.buffer()) {
204 auto line = slangSourceManager.getLineNumber(loc) - 1;
205 auto column = slangSourceManager.getColumnNumber(loc) - 1;
206 auto it = loc.buffer();
208 return llvm::lsp::Location(
uri, llvm::lsp::Range(Position(line, column)));
210 llvm::StringRef fileName = slangSourceManager.getFileName(loc);
212 llvm::SmallString<256> abs(fileName);
213 if (!llvm::sys::path::is_absolute(abs)) {
215 if (std::error_code ec = llvm::sys::fs::real_path(fileName, abs)) {
217 llvm::sys::fs::current_path(abs);
218 llvm::sys::path::append(abs, fileName);
222 if (
auto uriOrErr = llvm::lsp::URIForFile::fromFile(abs)) {
223 if (
auto e = uriOrErr.takeError())
224 return llvm::lsp::Location();
225 return llvm::lsp::Location(*uriOrErr,
226 llvm::lsp::Range(Position(line, column)));
228 return llvm::lsp::Location();
230 return llvm::lsp::Location();
239 if (start.uri != end.uri)
240 return llvm::lsp::Location();
242 return llvm::lsp::Location(
243 start.uri, llvm::lsp::Range(start.range.start, end.range.end));
246std::optional<std::pair<slang::BufferID, llvm::SmallString<128>>>
251 return fileInfo->second;
253 auto getIfExist = [&](StringRef path)
254 -> std::optional<std::pair<slang::BufferID, SmallString<128>>> {
255 if (llvm::sys::fs::exists(path)) {
256 auto memoryBuffer = llvm::MemoryBuffer::getFile(path);
261 auto newSlangBuffer =
driver.sourceManager.assignText(
262 path.str(), memoryBuffer.get()->getBufferStart());
263 driver.sourceLoader.addBuffer(newSlangBuffer);
266 .insert(std::make_pair(
267 filePath, std::make_pair(newSlangBuffer.id, path)))
270 return fileInfo->second;
275 if (llvm::sys::path::is_absolute(filePath))
276 return getIfExist(filePath);
279 for (
auto &libRoot :
globalContext.options.extraSourceLocationDirs) {
280 SmallString<128> lib(libRoot);
281 llvm::sys::path::append(lib, filePath);
282 if (
auto fileInfo = getIfExist(lib))
289static llvm::lsp::Range
getRange(
const mlir::FileLineColRange &fileLoc) {
290 return llvm::lsp::Range(
291 llvm::lsp::Position(fileLoc.getStartLine(), fileLoc.getStartColumn()),
292 llvm::lsp::Position(fileLoc.getEndLine(), fileLoc.getEndColumn()));
300 for (
size_t i = 0; i < text.size(); ++i) {
301 if (text[i] ==
'\n') {
302 lineOffsets.push_back(
static_cast<uint32_t
>(i + 1));
308std::optional<uint32_t>
320 size_t lineEnd = ((unsigned)(pos.line + 1) <
lineOffsets.size())
324 const llvm::UTF8 *src =
325 reinterpret_cast<const llvm::UTF8 *
>(text.data() + lineStart);
326 const llvm::UTF8 *srcEnd =
327 reinterpret_cast<const llvm::UTF8 *
>(text.data() + lineEnd);
330 const uint32_t target = pos.character;
332 return static_cast<uint32_t
>(
333 src -
reinterpret_cast<const llvm::UTF8 *
>(text.data()));
335 std::vector<llvm::UTF16> sink(target);
336 llvm::UTF16 *out = sink.data();
337 llvm::UTF16 *outEnd = out + sink.size();
339 (void)llvm::ConvertUTF8toUTF16(&src, srcEnd, &out, outEnd,
340 llvm::lenientConversion);
342 return static_cast<uint32_t
>(
reinterpret_cast<const char *
>(src) -
350 if (!slangBufferOffset.has_value())
353 uint32_t offset = slangBufferOffset.value();
358 const llvm::lsp::URIForFile &
uri,
const llvm::lsp::Position &defPos,
359 std::vector<llvm::lsp::Location> &locations) {
366 const auto &intervalMap =
index->getIntervalMap();
367 auto it = intervalMap.find(slangBufferPointer);
370 if (!it.valid() || slangBufferPointer < it.start())
373 auto element = it.value();
374 if (
auto attr = dyn_cast<Attribute>(element)) {
377 if (
auto fileLoc = dyn_cast<mlir::FileLineColRange>(attr)) {
380 auto fileInfo =
getOrOpenFile(fileLoc.getFilename().getValue());
383 const auto &[bufferId, filePath] = *fileInfo;
384 auto uri = llvm::lsp::URIForFile::fromFile(filePath);
385 if (
auto e =
uri.takeError()) {
389 locations.emplace_back(
uri.get(),
getRange(fileLoc));
396 const auto *symbol = cast<const slang::ast::Symbol *>(element);
398 slang::SourceRange range(symbol->location,
400 (symbol->name.size() ? symbol->name.size() : 1));
405 const llvm::lsp::URIForFile &
uri,
const llvm::lsp::Position &pos,
406 std::vector<llvm::lsp::Location> &references) {
412 const auto &intervalMap =
index->getIntervalMap();
413 auto intervalIt = intervalMap.find(slangBufferPointer);
415 if (!intervalIt.valid() || slangBufferPointer < intervalIt.start())
418 const auto *symbol = dyn_cast<const slang::ast::Symbol *>(intervalIt.value());
422 auto it =
index->getReferences().find(symbol);
423 if (it ==
index->getReferences().end())
425 for (
auto referenceRange : it->second)
static std::unique_ptr< Context > context
static llvm::lsp::Range getRange(const mlir::FileLineColRange &fileLoc)
static void copyBuffers(slang::driver::Driver &driver, const slang::driver::Driver *const projectDriver, const llvm::SmallString< 256 > &mainBufferFileName)
static void setTopModules(slang::driver::Driver &driver)
std::unique_ptr< circt::lsp::VerilogIndex > index
The index of the parsed module.
slang::BufferID mainBufferId
const char * getPointerFor(const llvm::lsp::Position &pos)
slang::driver::Driver driver
VerilogDocument(VerilogServerContext &globalContext, const llvm::lsp::URIForFile &uri, llvm::StringRef contents, std::vector< llvm::lsp::Diagnostic > &diagnostics, const slang::driver::Driver *projectDriver=nullptr, const std::vector< std::string > &projectIncludeDirectories={})
VerilogServerContext & globalContext
llvm::lsp::URIForFile uri
void getLocationsOf(const llvm::lsp::URIForFile &uri, const llvm::lsp::Position &defPos, std::vector< llvm::lsp::Location > &locations)
void computeLineOffsets(std::string_view text)
Build a vector of line start offsets (0-based).
void findReferencesOf(const llvm::lsp::URIForFile &uri, const llvm::lsp::Position &pos, std::vector< llvm::lsp::Location > &references)
std::optional< uint32_t > lspPositionToOffset(const llvm::lsp::Position &pos)
const slang::SourceManager & getSlangSourceManager() const
llvm::StringMap< std::pair< slang::BufferID, llvm::SmallString< 128 > > > filePathMap
std::vector< uint32_t > lineOffsets
The precomputed line offsets for faster lookups.
std::optional< std::pair< slang::BufferID, llvm::SmallString< 128 > > > getOrOpenFile(llvm::StringRef filePath)
llvm::lsp::Location getLspLocation(slang::SourceLocation loc) const
void error(Twine message)