1//===- TableGenServer.cpp - TableGen Language Server ----------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "TableGenServer.h"
10
11#include "mlir/Support/IndentedOstream.h"
12#include "mlir/Tools/lsp-server-support/CompilationDatabase.h"
13#include "mlir/Tools/lsp-server-support/Logging.h"
14#include "mlir/Tools/lsp-server-support/Protocol.h"
15#include "mlir/Tools/lsp-server-support/SourceMgrUtils.h"
16#include "llvm/ADT/IntervalMap.h"
17#include "llvm/ADT/PointerUnion.h"
18#include "llvm/ADT/StringMap.h"
19#include "llvm/Support/Path.h"
20#include "llvm/TableGen/Parser.h"
21#include "llvm/TableGen/Record.h"
22#include <optional>
23
24using namespace mlir;
25using llvm::Record;
26using llvm::RecordKeeper;
27using llvm::RecordVal;
28using llvm::SourceMgr;
29
30/// Returns the range of a lexical token given a SMLoc corresponding to the
31/// start of an token location. The range is computed heuristically, and
32/// supports identifier-like tokens, strings, etc.
33static SMRange convertTokenLocToRange(SMLoc loc) {
34 return lsp::convertTokenLocToRange(loc, identifierChars: "$");
35}
36
37/// Returns a language server uri for the given source location. `mainFileURI`
38/// corresponds to the uri for the main file of the source manager.
39static lsp::URIForFile getURIFromLoc(const SourceMgr &mgr, SMLoc loc,
40 const lsp::URIForFile &mainFileURI) {
41 int bufferId = mgr.FindBufferContainingLoc(Loc: loc);
42 if (bufferId == 0 || bufferId == static_cast<int>(mgr.getMainFileID()))
43 return mainFileURI;
44 llvm::Expected<lsp::URIForFile> fileForLoc = lsp::URIForFile::fromFile(
45 absoluteFilepath: mgr.getBufferInfo(i: bufferId).Buffer->getBufferIdentifier());
46 if (fileForLoc)
47 return *fileForLoc;
48 lsp::Logger::error(fmt: "Failed to create URI for include file: {0}",
49 vals: llvm::toString(E: fileForLoc.takeError()));
50 return mainFileURI;
51}
52
53/// Returns a language server location from the given source range.
54static lsp::Location getLocationFromLoc(SourceMgr &mgr, SMRange loc,
55 const lsp::URIForFile &uri) {
56 return lsp::Location(getURIFromLoc(mgr, loc: loc.Start, mainFileURI: uri),
57 lsp::Range(mgr, loc));
58}
59static lsp::Location getLocationFromLoc(SourceMgr &mgr, SMLoc loc,
60 const lsp::URIForFile &uri) {
61 return getLocationFromLoc(mgr, loc: convertTokenLocToRange(loc), uri);
62}
63
64/// Convert the given TableGen diagnostic to the LSP form.
65static std::optional<lsp::Diagnostic>
66getLspDiagnoticFromDiag(const llvm::SMDiagnostic &diag,
67 const lsp::URIForFile &uri) {
68 auto *sourceMgr = const_cast<SourceMgr *>(diag.getSourceMgr());
69 if (!sourceMgr || !diag.getLoc().isValid())
70 return std::nullopt;
71
72 lsp::Diagnostic lspDiag;
73 lspDiag.source = "tablegen";
74 lspDiag.category = "Parse Error";
75
76 // Try to grab a file location for this diagnostic.
77 lsp::Location loc = getLocationFromLoc(mgr&: *sourceMgr, loc: diag.getLoc(), uri);
78 lspDiag.range = loc.range;
79
80 // Skip diagnostics that weren't emitted within the main file.
81 if (loc.uri != uri)
82 return std::nullopt;
83
84 // Convert the severity for the diagnostic.
85 switch (diag.getKind()) {
86 case SourceMgr::DK_Warning:
87 lspDiag.severity = lsp::DiagnosticSeverity::Warning;
88 break;
89 case SourceMgr::DK_Error:
90 lspDiag.severity = lsp::DiagnosticSeverity::Error;
91 break;
92 case SourceMgr::DK_Note:
93 // Notes are emitted separately from the main diagnostic, so we just treat
94 // them as remarks given that we can't determine the diagnostic to relate
95 // them to.
96 case SourceMgr::DK_Remark:
97 lspDiag.severity = lsp::DiagnosticSeverity::Information;
98 break;
99 }
100 lspDiag.message = diag.getMessage().str();
101
102 return lspDiag;
103}
104
105/// Get the base definition of the given record value, or nullptr if one
106/// couldn't be found.
107static std::pair<const Record *, const RecordVal *>
108getBaseValue(const Record *record, const RecordVal *value) {
109 if (value->isTemplateArg())
110 return {nullptr, nullptr};
111
112 // Find a base value for the field in the super classes of the given record.
113 // On success, `record` is updated to the new parent record.
114 StringRef valueName = value->getName();
115 auto findValueInSupers = [&](const Record *&record) -> const RecordVal * {
116 for (const Record *parentRecord : record->getSuperClasses()) {
117 if (auto *newBase = parentRecord->getValue(Name: valueName)) {
118 record = parentRecord;
119 return newBase;
120 }
121 }
122 return nullptr;
123 };
124
125 // Try to find the lowest definition of the record value.
126 std::pair<const Record *, const RecordVal *> baseValue = {};
127 while (const RecordVal *newBase = findValueInSupers(record))
128 baseValue = {record, newBase};
129
130 // Check that the base isn't the same as the current value (e.g. if the value
131 // wasn't overridden).
132 if (!baseValue.second || baseValue.second->getLoc() == value->getLoc())
133 return {nullptr, nullptr};
134 return baseValue;
135}
136
137//===----------------------------------------------------------------------===//
138// TableGenIndex
139//===----------------------------------------------------------------------===//
140
141namespace {
142/// This class represents a single symbol definition within a TableGen index. It
143/// contains the definition of the symbol, the location of the symbol, and any
144/// recorded references.
145struct TableGenIndexSymbol {
146 TableGenIndexSymbol(const Record *record)
147 : definition(record),
148 defLoc(convertTokenLocToRange(loc: record->getLoc().front())) {}
149 TableGenIndexSymbol(const RecordVal *value)
150 : definition(value), defLoc(convertTokenLocToRange(loc: value->getLoc())) {}
151 virtual ~TableGenIndexSymbol() = default;
152
153 // The main definition of the symbol.
154 PointerUnion<const Record *, const RecordVal *> definition;
155
156 /// The source location of the definition.
157 SMRange defLoc;
158
159 /// The source location of the references of the definition.
160 SmallVector<SMRange> references;
161};
162/// This class represents a single record symbol.
163struct TableGenRecordSymbol : public TableGenIndexSymbol {
164 TableGenRecordSymbol(const Record *record) : TableGenIndexSymbol(record) {}
165 ~TableGenRecordSymbol() override = default;
166
167 static bool classof(const TableGenIndexSymbol *symbol) {
168 return isa<const Record *>(Val: symbol->definition);
169 }
170
171 /// Return the value of this symbol.
172 const Record *getValue() const { return cast<const Record *>(Val: definition); }
173};
174/// This class represents a single record value symbol.
175struct TableGenRecordValSymbol : public TableGenIndexSymbol {
176 TableGenRecordValSymbol(const Record *record, const RecordVal *value)
177 : TableGenIndexSymbol(value), record(record) {}
178 ~TableGenRecordValSymbol() override = default;
179
180 static bool classof(const TableGenIndexSymbol *symbol) {
181 return isa<const RecordVal *>(Val: symbol->definition);
182 }
183
184 /// Return the value of this symbol.
185 const RecordVal *getValue() const {
186 return cast<const RecordVal *>(Val: definition);
187 }
188
189 /// The parent record of this symbol.
190 const Record *record;
191};
192
193/// This class provides an index for definitions/uses within a TableGen
194/// document. It provides efficient lookup of a definition given an input source
195/// range.
196class TableGenIndex {
197public:
198 TableGenIndex() : intervalMap(allocator) {}
199
200 /// Initialize the index with the given RecordKeeper.
201 void initialize(const RecordKeeper &records);
202
203 /// Lookup a symbol for the given location. Returns nullptr if no symbol could
204 /// be found. If provided, `overlappedRange` is set to the range that the
205 /// provided `loc` overlapped with.
206 const TableGenIndexSymbol *lookup(SMLoc loc,
207 SMRange *overlappedRange = nullptr) const;
208
209private:
210 /// The type of interval map used to store source references. SMRange is
211 /// half-open, so we also need to use a half-open interval map.
212 using MapT = llvm::IntervalMap<
213 const char *, const TableGenIndexSymbol *,
214 llvm::IntervalMapImpl::NodeSizer<const char *,
215 const TableGenIndexSymbol *>::LeafSize,
216 llvm::IntervalMapHalfOpenInfo<const char *>>;
217
218 /// Get or insert a symbol for the given record.
219 TableGenIndexSymbol *getOrInsertDef(const Record *record) {
220 auto it = defToSymbol.try_emplace(Key: record, Args: nullptr);
221 if (it.second)
222 it.first->second = std::make_unique<TableGenRecordSymbol>(args&: record);
223 return &*it.first->second;
224 }
225 /// Get or insert a symbol for the given record value.
226 TableGenIndexSymbol *getOrInsertDef(const Record *record,
227 const RecordVal *value) {
228 auto it = defToSymbol.try_emplace(Key: value, Args: nullptr);
229 if (it.second) {
230 it.first->second =
231 std::make_unique<TableGenRecordValSymbol>(args&: record, args&: value);
232 }
233 return &*it.first->second;
234 }
235
236 /// An allocator for the interval map.
237 MapT::Allocator allocator;
238
239 /// An interval map containing a corresponding definition mapped to a source
240 /// interval.
241 MapT intervalMap;
242
243 /// A mapping between definitions and their corresponding symbol.
244 DenseMap<const void *, std::unique_ptr<TableGenIndexSymbol>> defToSymbol;
245};
246} // namespace
247
248void TableGenIndex::initialize(const RecordKeeper &records) {
249 intervalMap.clear();
250 defToSymbol.clear();
251
252 auto insertRef = [&](TableGenIndexSymbol *sym, SMRange refLoc,
253 bool isDef = false) {
254 const char *startLoc = refLoc.Start.getPointer();
255 const char *endLoc = refLoc.End.getPointer();
256
257 // If the location we got was empty, try to lex a token from the start
258 // location.
259 if (startLoc == endLoc) {
260 refLoc = convertTokenLocToRange(loc: SMLoc::getFromPointer(Ptr: startLoc));
261 startLoc = refLoc.Start.getPointer();
262 endLoc = refLoc.End.getPointer();
263
264 // If the location is still empty, bail on trying to use this reference
265 // location.
266 if (startLoc == endLoc)
267 return;
268 }
269
270 // Check to see if a symbol is already attached to this location.
271 // IntervalMap doesn't allow overlapping inserts, and we don't really
272 // want multiple symbols attached to a source location anyways. This
273 // shouldn't really happen in practice, but we should handle it gracefully.
274 if (!intervalMap.overlaps(a: startLoc, b: endLoc))
275 intervalMap.insert(a: startLoc, b: endLoc, y: sym);
276
277 if (!isDef)
278 sym->references.push_back(Elt: refLoc);
279 };
280 auto classes =
281 llvm::make_pointee_range(Range: llvm::make_second_range(c: records.getClasses()));
282 auto defs =
283 llvm::make_pointee_range(Range: llvm::make_second_range(c: records.getDefs()));
284 for (const Record &def : llvm::concat<Record>(Ranges&: classes, Ranges&: defs)) {
285 auto *sym = getOrInsertDef(record: &def);
286 insertRef(sym, sym->defLoc, /*isDef=*/true);
287
288 // Add references to the definition.
289 for (SMLoc loc : def.getLoc().drop_front())
290 insertRef(sym, convertTokenLocToRange(loc));
291 for (SMRange loc : def.getReferenceLocs())
292 insertRef(sym, loc);
293
294 // Add definitions for any values.
295 for (const RecordVal &value : def.getValues()) {
296 auto *sym = getOrInsertDef(record: &def, value: &value);
297 insertRef(sym, sym->defLoc, /*isDef=*/true);
298 for (SMRange refLoc : value.getReferenceLocs())
299 insertRef(sym, refLoc);
300 }
301 }
302}
303
304const TableGenIndexSymbol *
305TableGenIndex::lookup(SMLoc loc, SMRange *overlappedRange) const {
306 auto it = intervalMap.find(x: loc.getPointer());
307 if (!it.valid() || loc.getPointer() < it.start())
308 return nullptr;
309
310 if (overlappedRange) {
311 *overlappedRange = SMRange(SMLoc::getFromPointer(Ptr: it.start()),
312 SMLoc::getFromPointer(Ptr: it.stop()));
313 }
314 return it.value();
315}
316
317//===----------------------------------------------------------------------===//
318// TableGenTextFile
319//===----------------------------------------------------------------------===//
320
321namespace {
322/// This class represents a text file containing one or more TableGen documents.
323class TableGenTextFile {
324public:
325 TableGenTextFile(const lsp::URIForFile &uri, StringRef fileContents,
326 int64_t version,
327 const std::vector<std::string> &extraIncludeDirs,
328 std::vector<lsp::Diagnostic> &diagnostics);
329
330 /// Return the current version of this text file.
331 int64_t getVersion() const { return version; }
332
333 /// Update the file to the new version using the provided set of content
334 /// changes. Returns failure if the update was unsuccessful.
335 LogicalResult update(const lsp::URIForFile &uri, int64_t newVersion,
336 ArrayRef<lsp::TextDocumentContentChangeEvent> changes,
337 std::vector<lsp::Diagnostic> &diagnostics);
338
339 //===--------------------------------------------------------------------===//
340 // Definitions and References
341 //===--------------------------------------------------------------------===//
342
343 void getLocationsOf(const lsp::URIForFile &uri, const lsp::Position &defPos,
344 std::vector<lsp::Location> &locations);
345 void findReferencesOf(const lsp::URIForFile &uri, const lsp::Position &pos,
346 std::vector<lsp::Location> &references);
347
348 //===--------------------------------------------------------------------===//
349 // Document Links
350 //===--------------------------------------------------------------------===//
351
352 void getDocumentLinks(const lsp::URIForFile &uri,
353 std::vector<lsp::DocumentLink> &links);
354
355 //===--------------------------------------------------------------------===//
356 // Hover
357 //===--------------------------------------------------------------------===//
358
359 std::optional<lsp::Hover> findHover(const lsp::URIForFile &uri,
360 const lsp::Position &hoverPos);
361 lsp::Hover buildHoverForRecord(const Record *record,
362 const SMRange &hoverRange);
363 lsp::Hover buildHoverForTemplateArg(const Record *record,
364 const RecordVal *value,
365 const SMRange &hoverRange);
366 lsp::Hover buildHoverForField(const Record *record, const RecordVal *value,
367 const SMRange &hoverRange);
368
369private:
370 /// Initialize the text file from the given file contents.
371 void initialize(const lsp::URIForFile &uri, int64_t newVersion,
372 std::vector<lsp::Diagnostic> &diagnostics);
373
374 /// The full string contents of the file.
375 std::string contents;
376
377 /// The version of this file.
378 int64_t version;
379
380 /// The include directories for this file.
381 std::vector<std::string> includeDirs;
382
383 /// The source manager containing the contents of the input file.
384 SourceMgr sourceMgr;
385
386 /// The record keeper containing the parsed tablegen constructs.
387 std::unique_ptr<RecordKeeper> recordKeeper;
388
389 /// The index of the parsed file.
390 TableGenIndex index;
391
392 /// The set of includes of the parsed file.
393 SmallVector<lsp::SourceMgrInclude> parsedIncludes;
394};
395} // namespace
396
397TableGenTextFile::TableGenTextFile(
398 const lsp::URIForFile &uri, StringRef fileContents, int64_t version,
399 const std::vector<std::string> &extraIncludeDirs,
400 std::vector<lsp::Diagnostic> &diagnostics)
401 : contents(fileContents.str()), version(version) {
402 // Build the set of include directories for this file.
403 llvm::SmallString<32> uriDirectory(uri.file());
404 llvm::sys::path::remove_filename(path&: uriDirectory);
405 includeDirs.push_back(x: uriDirectory.str().str());
406 llvm::append_range(C&: includeDirs, R: extraIncludeDirs);
407
408 // Initialize the file.
409 initialize(uri, newVersion: version, diagnostics);
410}
411
412LogicalResult
413TableGenTextFile::update(const lsp::URIForFile &uri, int64_t newVersion,
414 ArrayRef<lsp::TextDocumentContentChangeEvent> changes,
415 std::vector<lsp::Diagnostic> &diagnostics) {
416 if (failed(Result: lsp::TextDocumentContentChangeEvent::applyTo(changes, contents))) {
417 lsp::Logger::error(fmt: "Failed to update contents of {0}", vals: uri.file());
418 return failure();
419 }
420
421 // If the file contents were properly changed, reinitialize the text file.
422 initialize(uri, newVersion, diagnostics);
423 return success();
424}
425
426void TableGenTextFile::initialize(const lsp::URIForFile &uri,
427 int64_t newVersion,
428 std::vector<lsp::Diagnostic> &diagnostics) {
429 version = newVersion;
430 sourceMgr = SourceMgr();
431 recordKeeper = std::make_unique<RecordKeeper>();
432
433 // Build a buffer for this file.
434 auto memBuffer = llvm::MemoryBuffer::getMemBuffer(InputData: contents, BufferName: uri.file());
435 if (!memBuffer) {
436 lsp::Logger::error(fmt: "Failed to create memory buffer for file", vals: uri.file());
437 return;
438 }
439 sourceMgr.setIncludeDirs(includeDirs);
440 sourceMgr.AddNewSourceBuffer(F: std::move(memBuffer), IncludeLoc: SMLoc());
441
442 // This class provides a context argument for the SourceMgr diagnostic
443 // handler.
444 struct DiagHandlerContext {
445 std::vector<lsp::Diagnostic> &diagnostics;
446 const lsp::URIForFile &uri;
447 } handlerContext{.diagnostics: diagnostics, .uri: uri};
448
449 // Set the diagnostic handler for the tablegen source manager.
450 sourceMgr.setDiagHandler(
451 DH: [](const llvm::SMDiagnostic &diag, void *rawHandlerContext) {
452 auto *ctx = reinterpret_cast<DiagHandlerContext *>(rawHandlerContext);
453 if (auto lspDiag = getLspDiagnoticFromDiag(diag, uri: ctx->uri))
454 ctx->diagnostics.push_back(x: *lspDiag);
455 },
456 Ctx: &handlerContext);
457 bool failedToParse = llvm::TableGenParseFile(InputSrcMgr&: sourceMgr, Records&: *recordKeeper);
458
459 // Process all of the include files.
460 lsp::gatherIncludeFiles(sourceMgr, includes&: parsedIncludes);
461 if (failedToParse)
462 return;
463
464 // If we successfully parsed the file, we can now build the index.
465 index.initialize(records: *recordKeeper);
466}
467
468//===----------------------------------------------------------------------===//
469// TableGenTextFile: Definitions and References
470//===----------------------------------------------------------------------===//
471
472void TableGenTextFile::getLocationsOf(const lsp::URIForFile &uri,
473 const lsp::Position &defPos,
474 std::vector<lsp::Location> &locations) {
475 SMLoc posLoc = defPos.getAsSMLoc(mgr&: sourceMgr);
476 const TableGenIndexSymbol *symbol = index.lookup(loc: posLoc);
477 if (!symbol)
478 return;
479
480 // If this symbol is a record value and the def position is already the def of
481 // the symbol, check to see if the value has a base definition. This allows
482 // for a "go-to-def" on a "let" to resolve the definition in the base class.
483 auto *valSym = dyn_cast<TableGenRecordValSymbol>(Val: symbol);
484 if (valSym && lsp::contains(range: valSym->defLoc, loc: posLoc)) {
485 if (auto *val = getBaseValue(record: valSym->record, value: valSym->getValue()).second) {
486 locations.push_back(x: getLocationFromLoc(mgr&: sourceMgr, loc: val->getLoc(), uri));
487 return;
488 }
489 }
490
491 locations.push_back(x: getLocationFromLoc(mgr&: sourceMgr, loc: symbol->defLoc, uri));
492}
493
494void TableGenTextFile::findReferencesOf(
495 const lsp::URIForFile &uri, const lsp::Position &pos,
496 std::vector<lsp::Location> &references) {
497 SMLoc posLoc = pos.getAsSMLoc(mgr&: sourceMgr);
498 const TableGenIndexSymbol *symbol = index.lookup(loc: posLoc);
499 if (!symbol)
500 return;
501
502 references.push_back(x: getLocationFromLoc(mgr&: sourceMgr, loc: symbol->defLoc, uri));
503 for (SMRange refLoc : symbol->references)
504 references.push_back(x: getLocationFromLoc(mgr&: sourceMgr, loc: refLoc, uri));
505}
506
507//===--------------------------------------------------------------------===//
508// TableGenTextFile: Document Links
509//===--------------------------------------------------------------------===//
510
511void TableGenTextFile::getDocumentLinks(const lsp::URIForFile &uri,
512 std::vector<lsp::DocumentLink> &links) {
513 for (const lsp::SourceMgrInclude &include : parsedIncludes)
514 links.emplace_back(args: include.range, args: include.uri);
515}
516
517//===----------------------------------------------------------------------===//
518// TableGenTextFile: Hover
519//===----------------------------------------------------------------------===//
520
521std::optional<lsp::Hover>
522TableGenTextFile::findHover(const lsp::URIForFile &uri,
523 const lsp::Position &hoverPos) {
524 // Check for a reference to an include.
525 for (const lsp::SourceMgrInclude &include : parsedIncludes)
526 if (include.range.contains(pos: hoverPos))
527 return include.buildHover();
528
529 // Find the symbol at the given location.
530 SMRange hoverRange;
531 SMLoc posLoc = hoverPos.getAsSMLoc(mgr&: sourceMgr);
532 const TableGenIndexSymbol *symbol = index.lookup(loc: posLoc, overlappedRange: &hoverRange);
533 if (!symbol)
534 return std::nullopt;
535
536 // Build hover for a Record.
537 if (auto *record = dyn_cast<TableGenRecordSymbol>(Val: symbol))
538 return buildHoverForRecord(record: record->getValue(), hoverRange);
539
540 // Build hover for a RecordVal, which is either a template argument or a
541 // field.
542 auto *recordVal = cast<TableGenRecordValSymbol>(Val: symbol);
543 const RecordVal *value = recordVal->getValue();
544 if (value->isTemplateArg())
545 return buildHoverForTemplateArg(record: recordVal->record, value, hoverRange);
546 return buildHoverForField(record: recordVal->record, value, hoverRange);
547}
548
549lsp::Hover TableGenTextFile::buildHoverForRecord(const Record *record,
550 const SMRange &hoverRange) {
551 lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
552 {
553 llvm::raw_string_ostream hoverOS(hover.contents.value);
554
555 // Format the type of record this is.
556 if (record->isClass()) {
557 hoverOS << "**class** `" << record->getName() << "`";
558 } else if (record->isAnonymous()) {
559 hoverOS << "**anonymous class**";
560 } else {
561 hoverOS << "**def** `" << record->getName() << "`";
562 }
563 hoverOS << "\n***\n";
564
565 // Check if this record has summary/description fields. These are often used
566 // to hold documentation for the record.
567 auto printAndFormatField = [&](StringRef fieldName) {
568 // Check that the record actually has the given field, and that it's a
569 // string.
570 const RecordVal *value = record->getValue(Name: fieldName);
571 if (!value || !value->getValue())
572 return;
573 auto *stringValue = dyn_cast<llvm::StringInit>(Val: value->getValue());
574 if (!stringValue)
575 return;
576
577 raw_indented_ostream ros(hoverOS);
578 ros.printReindented(str: stringValue->getValue().rtrim(Chars: " \t"));
579 hoverOS << "\n***\n";
580 };
581 printAndFormatField("summary");
582 printAndFormatField("description");
583
584 // Check for documentation in the source file.
585 if (std::optional<std::string> doc =
586 lsp::extractSourceDocComment(sourceMgr, loc: record->getLoc().front())) {
587 hoverOS << "\n" << *doc << "\n";
588 }
589 }
590 return hover;
591}
592
593lsp::Hover TableGenTextFile::buildHoverForTemplateArg(
594 const Record *record, const RecordVal *value, const SMRange &hoverRange) {
595 lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
596 {
597 llvm::raw_string_ostream hoverOS(hover.contents.value);
598 StringRef name = value->getName().rsplit(Separator: ':').second;
599
600 hoverOS << "**template arg** `" << name << "`\n***\nType: `";
601 value->getType()->print(OS&: hoverOS);
602 hoverOS << "`\n";
603 }
604 return hover;
605}
606
607lsp::Hover TableGenTextFile::buildHoverForField(const Record *record,
608 const RecordVal *value,
609 const SMRange &hoverRange) {
610 lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
611 {
612 llvm::raw_string_ostream hoverOS(hover.contents.value);
613 hoverOS << "**field** `" << value->getName() << "`\n***\nType: `";
614 value->getType()->print(OS&: hoverOS);
615 hoverOS << "`\n***\n";
616
617 // Check for documentation in the source file.
618 if (std::optional<std::string> doc =
619 lsp::extractSourceDocComment(sourceMgr, loc: value->getLoc())) {
620 hoverOS << "\n" << *doc << "\n";
621 hoverOS << "\n***\n";
622 }
623
624 // Check to see if there is a base value that we can use for
625 // documentation.
626 auto [baseRecord, baseValue] = getBaseValue(record, value);
627 if (baseValue) {
628 if (std::optional<std::string> doc =
629 lsp::extractSourceDocComment(sourceMgr, loc: baseValue->getLoc())) {
630 hoverOS << "\n *From `" << baseRecord->getName() << "`*:\n\n"
631 << *doc << "\n";
632 }
633 }
634 }
635 return hover;
636}
637
638//===----------------------------------------------------------------------===//
639// TableGenServer::Impl
640//===----------------------------------------------------------------------===//
641
642struct lsp::TableGenServer::Impl {
643 explicit Impl(const Options &options)
644 : options(options), compilationDatabase(options.compilationDatabases) {}
645
646 /// TableGen LSP options.
647 const Options &options;
648
649 /// The compilation database containing additional information for files
650 /// passed to the server.
651 lsp::CompilationDatabase compilationDatabase;
652
653 /// The files held by the server, mapped by their URI file name.
654 llvm::StringMap<std::unique_ptr<TableGenTextFile>> files;
655};
656
657//===----------------------------------------------------------------------===//
658// TableGenServer
659//===----------------------------------------------------------------------===//
660
661lsp::TableGenServer::TableGenServer(const Options &options)
662 : impl(std::make_unique<Impl>(args: options)) {}
663lsp::TableGenServer::~TableGenServer() = default;
664
665void lsp::TableGenServer::addDocument(const URIForFile &uri, StringRef contents,
666 int64_t version,
667 std::vector<Diagnostic> &diagnostics) {
668 // Build the set of additional include directories.
669 std::vector<std::string> additionalIncludeDirs = impl->options.extraDirs;
670 const auto &fileInfo = impl->compilationDatabase.getFileInfo(filename: uri.file());
671 llvm::append_range(C&: additionalIncludeDirs, R: fileInfo.includeDirs);
672
673 impl->files[uri.file()] = std::make_unique<TableGenTextFile>(
674 args: uri, args&: contents, args&: version, args&: additionalIncludeDirs, args&: diagnostics);
675}
676
677void lsp::TableGenServer::updateDocument(
678 const URIForFile &uri, ArrayRef<TextDocumentContentChangeEvent> changes,
679 int64_t version, std::vector<Diagnostic> &diagnostics) {
680 // Check that we actually have a document for this uri.
681 auto it = impl->files.find(Key: uri.file());
682 if (it == impl->files.end())
683 return;
684
685 // Try to update the document. If we fail, erase the file from the server. A
686 // failed updated generally means we've fallen out of sync somewhere.
687 if (failed(Result: it->second->update(uri, newVersion: version, changes, diagnostics)))
688 impl->files.erase(I: it);
689}
690
691std::optional<int64_t>
692lsp::TableGenServer::removeDocument(const URIForFile &uri) {
693 auto it = impl->files.find(Key: uri.file());
694 if (it == impl->files.end())
695 return std::nullopt;
696
697 int64_t version = it->second->getVersion();
698 impl->files.erase(I: it);
699 return version;
700}
701
702void lsp::TableGenServer::getLocationsOf(const URIForFile &uri,
703 const Position &defPos,
704 std::vector<Location> &locations) {
705 auto fileIt = impl->files.find(Key: uri.file());
706 if (fileIt != impl->files.end())
707 fileIt->second->getLocationsOf(uri, defPos, locations);
708}
709
710void lsp::TableGenServer::findReferencesOf(const URIForFile &uri,
711 const Position &pos,
712 std::vector<Location> &references) {
713 auto fileIt = impl->files.find(Key: uri.file());
714 if (fileIt != impl->files.end())
715 fileIt->second->findReferencesOf(uri, pos, references);
716}
717
718void lsp::TableGenServer::getDocumentLinks(
719 const URIForFile &uri, std::vector<DocumentLink> &documentLinks) {
720 auto fileIt = impl->files.find(Key: uri.file());
721 if (fileIt != impl->files.end())
722 return fileIt->second->getDocumentLinks(uri, links&: documentLinks);
723}
724
725std::optional<lsp::Hover>
726lsp::TableGenServer::findHover(const URIForFile &uri,
727 const Position &hoverPos) {
728 auto fileIt = impl->files.find(Key: uri.file());
729 if (fileIt != impl->files.end())
730 return fileIt->second->findHover(uri, hoverPos);
731 return std::nullopt;
732}
733

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of mlir/lib/Tools/tblgen-lsp-server/TableGenServer.cpp