[llvm] [llvm-lsp] LSP server for LLVM IR (PR #161969)
Michal Vlasák via llvm-commits
llvm-commits at lists.llvm.org
Wed Dec 17 09:26:16 PST 2025
================
@@ -0,0 +1,213 @@
+//===-- llvm-lsp-server.cpp -------------------------------------*- C++ -*-===//
+//
+// 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 "llvm/IR/BasicBlock.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/Program.h"
+
+#include "IRDocument.h"
+#include "llvm-lsp-server.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/raw_ostream.h"
+#include <string>
+
+using namespace llvm;
+
+static cl::OptionCategory LlvmLspServerCategory("llvm-lsp-server options");
+static cl::opt<std::string> LogFilePath("log-file",
+ cl::desc("Path to log file"),
+ cl::init("/tmp/llvm-lsp-server.log"),
+ cl::cat(LlvmLspServerCategory));
+
+static lsp::Position llvmFileLocToLspPosition(const FileLoc &Pos) {
+ return lsp::Position(Pos.Line, Pos.Col);
+}
+
+static lsp::Range llvmFileLocRangeToLspRange(const FileLocRange &Range) {
+ return lsp::Range(llvmFileLocToLspPosition(Range.Start),
+ llvmFileLocToLspPosition(Range.End));
+}
+
+llvm::Error LspServer::run() {
+ registerMessageHandlers();
+ return Transport.run(MessageHandler);
+}
+
+void LspServer::sendInfo(const std::string &Message) {
+ ShowMessageSender(lsp::ShowMessageParams(lsp::MessageType::Info, Message));
+}
+
+void LspServer::sendError(const std::string &Message) {
+ ShowMessageSender(lsp::ShowMessageParams(lsp::MessageType::Error, Message));
+}
+
+void LspServer::handleRequestInitialize(
+ const lsp::InitializeParams &Params,
+ lsp::Callback<llvm::json::Value> Reply) {
+ lsp::Logger::info("Received Initialize Message!");
+ sendInfo("Hello! Welcome to LLVM IR Language Server!");
+
+ // clang-format off
+ json::Object ResponseParams{
+ {"capabilities",
+ json::Object{
+ {"textDocumentSync",
+ json::Object{
+ {"openClose", true},
+ {"change", 0}, // We dont want to sync the documents.
+ }
+ },
+ {"referencesProvider", true},
+ {"documentSymbolProvider", true},
+ }
+ }
+ };
+ // clang-format on
+ Reply(json::Value(std::move(ResponseParams)));
+}
+
+void LspServer::handleNotificationTextDocumentDidOpen(
+ const lsp::DidOpenTextDocumentParams &Params) {
+ lsp::Logger::info("Received didOpen Message!");
+ StringRef Filepath = Params.textDocument.uri.file();
+ sendInfo("LLVM Language Server Recognized that you opened " + Filepath.str());
+
+ // Prepare IRDocument for Queries
+ lsp::Logger::info("Creating IRDocument for {}", Filepath.str());
+ OpenDocuments[Filepath.str()] = std::make_unique<IRDocument>(Filepath.str());
+}
+
+void LspServer::handleRequestGetReferences(
+ const lsp::ReferenceParams &Params,
+ lsp::Callback<std::vector<lsp::Location>> Reply) {
+ auto Filepath = Params.textDocument.uri.file();
+ auto Line = Params.position.line;
+ auto Character = Params.position.character;
+ assert(Line >= 0);
+ assert(Character >= 0);
+ std::stringstream SS;
+ std::vector<lsp::Location> Result;
+ const auto &Doc = OpenDocuments[Filepath.str()];
+ if (Instruction *MaybeI = Doc->getInstructionAtLocation(Line, Character)) {
+ auto TryAddReference = [&Result, &Params, &Doc](Instruction *I) {
+ auto MaybeInstLocation = Doc->ParserContext.getInstructionLocation(I);
+ if (!MaybeInstLocation)
+ return;
+ Result.emplace_back(
+ lsp::Location(Params.textDocument.uri,
+ llvmFileLocRangeToLspRange(MaybeInstLocation.value())));
+ };
+ TryAddReference(MaybeI);
+ for (User *U : MaybeI->users()) {
+ if (auto *UserInst = dyn_cast<Instruction>(U)) {
+ TryAddReference(UserInst);
+ }
+ }
+ }
+
+ Reply(std::move(Result));
+}
+
+void LspServer::handleRequestTextDocumentDocumentSymbol(
+ const lsp::DocumentSymbolParams &Params,
+ lsp::Callback<std::vector<lsp::DocumentSymbol>> Reply) {
+ if (OpenDocuments.find(Params.textDocument.uri.file().str()) ==
+ OpenDocuments.end()) {
+ lsp::Logger::error(
+ "Document in textDocument/documentSymbol request not open: {}",
+ Params.textDocument.uri.file());
+ return Reply(
+ make_error<lsp::LSPError>(formatv("Did not open file previously {}",
+ Params.textDocument.uri.file()),
+ lsp::ErrorCode::InvalidParams));
+ }
+ auto &Doc = OpenDocuments[Params.textDocument.uri.file().str()];
+ std::vector<lsp::DocumentSymbol> Result;
+ for (const auto &Fn : Doc->getFunctions()) {
+ lsp::DocumentSymbol Func;
+ Func.name = Fn.getNameOrAsOperand();
+ Func.kind = lsp::SymbolKind::Function;
+ auto MaybeLoc = Doc->ParserContext.getFunctionLocation(&Fn);
+ if (!MaybeLoc)
+ continue;
+ Func.range = llvmFileLocRangeToLspRange(*MaybeLoc);
+ Func.selectionRange = Func.range;
----------------
vlasakm wrote:
What's the difference betwee nrange and selectionRange? Should we do something different here?
https://github.com/llvm/llvm-project/pull/161969
More information about the llvm-commits
mailing list