Skip to content

Commit 5de12bb

Browse files
committed
[mlir][Tablegen-LSP] Add support for a basic TableGen language server
This follows the same general structure of the MLIR and PDLL language servers. This commits adds the basic functionality for setting up the server, and initially only supports providing diagnostics. Followon commits will build out more comprehensive behavior. Realistically this should eventually live in llvm/, but building in MLIR is an easier initial step given that: * All of the necessary LSP functionality is already here * It allows for proving out useful language features (e.g. compilation databases) without affecting wider scale tablegen users * MLIR has a vscode extension that can immediately take advantage of it Differential Revision: https://reviews.llvm.org/D125440
1 parent 45e01ce commit 5de12bb

27 files changed

+872
-3
lines changed

mlir/docs/Tools/MLIRLSP.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,8 @@ extra setup steps are required:
176176
177177
* Copy `mlir/utils/textmate/mlir.json` to the extension directory and rename
178178
to `grammar.json`.
179+
* Copy `llvm/utils/textmate/tablegen.json` to the extension directory and rename
180+
to `tablegen-grammar.json`.
179181
* Copy
180182
`https://mlir.llvm.org//LogoAssets/logo/PNG/full_color/mlir-identity-03.png`
181183
to the extension directory and rename to `icon.png`.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//===- TableGenLSPServerMain.h - TableGen Language Server main --*- C++ -*-===//
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+
// Main entry function for tblgen-lsp-server when built as standalone binary.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef MLIR_TOOLS_TBLGENLSPSERVER_TABLEGENLSPSERVERMAIN_H
14+
#define MLIR_TOOLS_TBLGENLSPSERVER_TABLEGENLSPSERVERMAIN_H
15+
16+
namespace mlir {
17+
struct LogicalResult;
18+
19+
/// Implementation for tools like `tblgen-lsp-server`.
20+
LogicalResult TableGenLspServerMain(int argc, char **argv);
21+
22+
} // namespace mlir
23+
24+
#endif // MLIR_TOOLS_TBLGENLSPSERVER_TABLEGENLSPSERVERMAIN_H

mlir/lib/Tools/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ add_subdirectory(mlir-pdll-lsp-server)
55
add_subdirectory(mlir-reduce)
66
add_subdirectory(mlir-translate)
77
add_subdirectory(PDLL)
8+
add_subdirectory(tblgen-lsp-server)

mlir/lib/Tools/lsp-server-support/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
add_mlir_library(MLIRLspServerSupportLib
22
Logging.cpp
33
Protocol.cpp
4+
SourceMgrUtils.cpp
45
Transport.cpp
56

67
ADDITIONAL_HEADER_DIRS
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//===--- SourceMgrUtils.cpp - SourceMgr LSP Utils -------------------------===//
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 "SourceMgrUtils.h"
10+
11+
using namespace mlir;
12+
using namespace mlir::lsp;
13+
14+
/// Find the end of a string whose contents start at the given `curPtr`. Returns
15+
/// the position at the end of the string, after a terminal or invalid character
16+
/// (e.g. `"` or `\0`).
17+
static const char *lexLocStringTok(const char *curPtr) {
18+
while (char c = *curPtr++) {
19+
// Check for various terminal characters.
20+
if (StringRef("\"\n\v\f").contains(c))
21+
return curPtr;
22+
23+
// Check for escape sequences.
24+
if (c == '\\') {
25+
// Check a few known escapes and \xx hex digits.
26+
if (*curPtr == '"' || *curPtr == '\\' || *curPtr == 'n' || *curPtr == 't')
27+
++curPtr;
28+
else if (llvm::isHexDigit(*curPtr) && llvm::isHexDigit(curPtr[1]))
29+
curPtr += 2;
30+
else
31+
return curPtr;
32+
}
33+
}
34+
35+
// If we hit this point, we've reached the end of the buffer. Update the end
36+
// pointer to not point past the buffer.
37+
return curPtr - 1;
38+
}
39+
40+
SMRange lsp::convertTokenLocToRange(SMLoc loc) {
41+
if (!loc.isValid())
42+
return SMRange();
43+
const char *curPtr = loc.getPointer();
44+
45+
// Check if this is a string token.
46+
if (*curPtr == '"') {
47+
curPtr = lexLocStringTok(curPtr + 1);
48+
49+
// Otherwise, default to handling an identifier.
50+
} else {
51+
// Return if the given character is a valid identifier character.
52+
auto isIdentifierChar = [](char c) {
53+
return isalnum(c) || c == '$' || c == '.' || c == '_' || c == '-';
54+
};
55+
56+
while (*curPtr && isIdentifierChar(*(++curPtr)))
57+
continue;
58+
}
59+
60+
return SMRange(loc, SMLoc::getFromPointer(curPtr));
61+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//===--- SourceMgrUtils.h - SourceMgr LSP Utils -----------------*- C++ -*-===//
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+
// This file contains an array of generally useful SourceMgr utilities for
10+
// interacting with LSP components.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#ifndef LIB_MLIR_TOOLS_LSPSERVERSUPPORT_TRANSPORT_H_
15+
#define LIB_MLIR_TOOLS_LSPSERVERSUPPORT_TRANSPORT_H_
16+
17+
#include "Protocol.h"
18+
#include "llvm/Support/SourceMgr.h"
19+
20+
namespace mlir {
21+
namespace lsp {
22+
23+
/// Returns the range of a lexical token given a SMLoc corresponding to the
24+
/// start of an token location. The range is computed heuristically, and
25+
/// supports identifier-like tokens, strings, etc.
26+
SMRange convertTokenLocToRange(SMLoc loc);
27+
28+
} // namespace lsp
29+
} // namespace mlir
30+
31+
#endif

mlir/lib/Tools/mlir-lsp-server/MLIRServer.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "MLIRServer.h"
1010
#include "../lsp-server-support/Logging.h"
1111
#include "../lsp-server-support/Protocol.h"
12+
#include "../lsp-server-support/SourceMgrUtils.h"
1213
#include "mlir/IR/FunctionInterfaces.h"
1314
#include "mlir/IR/Operation.h"
1415
#include "mlir/Parser/AsmParserState.h"
@@ -55,7 +56,7 @@ getLocationFromLoc(llvm::SourceMgr &sourceMgr, Location loc,
5556
// Use range of potential identifier starting at location, else length 1
5657
// range.
5758
location->range.end.character += 1;
58-
if (Optional<SMRange> range = AsmParserState::convertIdLocToRange(loc)) {
59+
if (Optional<SMRange> range = lsp::convertTokenLocToRange(loc)) {
5960
auto lineCol = sourceMgr.getLineAndColumn(range->End);
6061
location->range.end.character =
6162
std::max(fileLoc.getColumn() + 1, lineCol.second - 1);
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
set(LLVM_LINK_COMPONENTS
2+
Demangle
3+
Support
4+
TableGen
5+
)
6+
7+
llvm_add_library(TableGenLspServerLib
8+
LSPServer.cpp
9+
TableGenServer.cpp
10+
TableGenLspServerMain.cpp
11+
12+
ADDITIONAL_HEADER_DIRS
13+
${MLIR_MAIN_INCLUDE_DIR}/mlir/Tools/tblgen-lsp-server
14+
15+
LINK_LIBS PUBLIC
16+
MLIRLspServerSupportLib
17+
MLIRSupport
18+
)
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
//===- LSPServer.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 "LSPServer.h"
10+
11+
#include "../lsp-server-support/Logging.h"
12+
#include "../lsp-server-support/Protocol.h"
13+
#include "../lsp-server-support/Transport.h"
14+
#include "TableGenServer.h"
15+
#include "llvm/ADT/FunctionExtras.h"
16+
#include "llvm/ADT/StringMap.h"
17+
18+
using namespace mlir;
19+
using namespace mlir::lsp;
20+
21+
//===----------------------------------------------------------------------===//
22+
// LSPServer
23+
//===----------------------------------------------------------------------===//
24+
25+
namespace {
26+
struct LSPServer {
27+
LSPServer(TableGenServer &server, JSONTransport &transport)
28+
: server(server), transport(transport) {}
29+
30+
//===--------------------------------------------------------------------===//
31+
// Initialization
32+
33+
void onInitialize(const InitializeParams &params,
34+
Callback<llvm::json::Value> reply);
35+
void onInitialized(const InitializedParams &params);
36+
void onShutdown(const NoParams &params, Callback<std::nullptr_t> reply);
37+
38+
//===--------------------------------------------------------------------===//
39+
// Document Change
40+
41+
void onDocumentDidOpen(const DidOpenTextDocumentParams &params);
42+
void onDocumentDidClose(const DidCloseTextDocumentParams &params);
43+
void onDocumentDidChange(const DidChangeTextDocumentParams &params);
44+
45+
//===--------------------------------------------------------------------===//
46+
// Fields
47+
//===--------------------------------------------------------------------===//
48+
49+
TableGenServer &server;
50+
JSONTransport &transport;
51+
52+
/// An outgoing notification used to send diagnostics to the client when they
53+
/// are ready to be processed.
54+
OutgoingNotification<PublishDiagnosticsParams> publishDiagnostics;
55+
56+
/// Used to indicate that the 'shutdown' request was received from the
57+
/// Language Server client.
58+
bool shutdownRequestReceived = false;
59+
};
60+
} // namespace
61+
62+
//===----------------------------------------------------------------------===//
63+
// Initialization
64+
65+
void LSPServer::onInitialize(const InitializeParams &params,
66+
Callback<llvm::json::Value> reply) {
67+
// Send a response with the capabilities of this server.
68+
llvm::json::Object serverCaps{
69+
{"textDocumentSync",
70+
llvm::json::Object{
71+
{"openClose", true},
72+
{"change", (int)TextDocumentSyncKind::Full},
73+
{"save", true},
74+
}},
75+
};
76+
77+
llvm::json::Object result{
78+
{{"serverInfo", llvm::json::Object{{"name", "tblgen-lsp-server"},
79+
{"version", "0.0.1"}}},
80+
{"capabilities", std::move(serverCaps)}}};
81+
reply(std::move(result));
82+
}
83+
void LSPServer::onInitialized(const InitializedParams &) {}
84+
void LSPServer::onShutdown(const NoParams &, Callback<std::nullptr_t> reply) {
85+
shutdownRequestReceived = true;
86+
reply(nullptr);
87+
}
88+
89+
//===----------------------------------------------------------------------===//
90+
// Document Change
91+
92+
void LSPServer::onDocumentDidOpen(const DidOpenTextDocumentParams &params) {
93+
PublishDiagnosticsParams diagParams(params.textDocument.uri,
94+
params.textDocument.version);
95+
server.addOrUpdateDocument(params.textDocument.uri, params.textDocument.text,
96+
params.textDocument.version,
97+
diagParams.diagnostics);
98+
99+
// Publish any recorded diagnostics.
100+
publishDiagnostics(diagParams);
101+
}
102+
void LSPServer::onDocumentDidClose(const DidCloseTextDocumentParams &params) {
103+
Optional<int64_t> version = server.removeDocument(params.textDocument.uri);
104+
if (!version)
105+
return;
106+
107+
// Empty out the diagnostics shown for this document. This will clear out
108+
// anything currently displayed by the client for this document (e.g. in the
109+
// "Problems" pane of VSCode).
110+
publishDiagnostics(
111+
PublishDiagnosticsParams(params.textDocument.uri, *version));
112+
}
113+
void LSPServer::onDocumentDidChange(const DidChangeTextDocumentParams &params) {
114+
// TODO: We currently only support full document updates, we should refactor
115+
// to avoid this.
116+
if (params.contentChanges.size() != 1)
117+
return;
118+
PublishDiagnosticsParams diagParams(params.textDocument.uri,
119+
params.textDocument.version);
120+
server.addOrUpdateDocument(
121+
params.textDocument.uri, params.contentChanges.front().text,
122+
params.textDocument.version, diagParams.diagnostics);
123+
124+
// Publish any recorded diagnostics.
125+
publishDiagnostics(diagParams);
126+
}
127+
128+
//===----------------------------------------------------------------------===//
129+
// Entry Point
130+
//===----------------------------------------------------------------------===//
131+
132+
LogicalResult mlir::lsp::runTableGenLSPServer(TableGenServer &server,
133+
JSONTransport &transport) {
134+
LSPServer lspServer(server, transport);
135+
MessageHandler messageHandler(transport);
136+
137+
// Initialization
138+
messageHandler.method("initialize", &lspServer, &LSPServer::onInitialize);
139+
messageHandler.notification("initialized", &lspServer,
140+
&LSPServer::onInitialized);
141+
messageHandler.method("shutdown", &lspServer, &LSPServer::onShutdown);
142+
143+
// Document Changes
144+
messageHandler.notification("textDocument/didOpen", &lspServer,
145+
&LSPServer::onDocumentDidOpen);
146+
messageHandler.notification("textDocument/didClose", &lspServer,
147+
&LSPServer::onDocumentDidClose);
148+
messageHandler.notification("textDocument/didChange", &lspServer,
149+
&LSPServer::onDocumentDidChange);
150+
151+
// Diagnostics
152+
lspServer.publishDiagnostics =
153+
messageHandler.outgoingNotification<PublishDiagnosticsParams>(
154+
"textDocument/publishDiagnostics");
155+
156+
// Run the main loop of the transport.
157+
if (llvm::Error error = transport.run(messageHandler)) {
158+
Logger::error("Transport error: {0}", error);
159+
llvm::consumeError(std::move(error));
160+
return failure();
161+
}
162+
return success(lspServer.shutdownRequestReceived);
163+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//===- LSPServer.h - TableGen LSP Server ------------------------*- C++ -*-===//
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+
#ifndef LIB_MLIR_TOOLS_TBLGENLSPSERVER_LSPSERVER_H
10+
#define LIB_MLIR_TOOLS_TBLGENLSPSERVER_LSPSERVER_H
11+
12+
#include <memory>
13+
14+
namespace mlir {
15+
struct LogicalResult;
16+
17+
namespace lsp {
18+
class JSONTransport;
19+
class TableGenServer;
20+
21+
/// Run the main loop of the LSP server using the given TableGen server and
22+
/// transport.
23+
LogicalResult runTableGenLSPServer(TableGenServer &server,
24+
JSONTransport &transport);
25+
26+
} // namespace lsp
27+
} // namespace mlir
28+
29+
#endif // LIB_MLIR_TOOLS_TBLGENLSPSERVER_LSPSERVER_H

0 commit comments

Comments
 (0)