From ecdf859b6ce481abfd530425dcf6f0f764bd0001 Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Tue, 28 Dec 2021 03:01:00 +0000 Subject: CTAN sync 202112280300 --- graphics/asymptote/lspserv.cc | 741 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 741 insertions(+) create mode 100644 graphics/asymptote/lspserv.cc (limited to 'graphics/asymptote/lspserv.cc') diff --git a/graphics/asymptote/lspserv.cc b/graphics/asymptote/lspserv.cc new file mode 100644 index 0000000000..3ad469d99b --- /dev/null +++ b/graphics/asymptote/lspserv.cc @@ -0,0 +1,741 @@ +// +// Created by Supakorn on 5/13/2021. +// + +#include "common.h" + +#ifdef HAVE_LSP + +#include "lspserv.h" + +#include + +#include +#include +#include +#include +#include + +#include + +#include "dec.h" +#include "process.h" +#include "locate.h" + +#define GC_THREADS + +#include "gc.h" + +#define REGISTER_REQ_FN(typ, fn) remoteEndPoint->registerHandler(\ + [this](typ::request const& req) { return this->fn(req); }); +#define REGISTER_NOTIF_FN(typ, handler) remoteEndPoint->registerHandler(\ + [this](typ::notify& notif) { this->handler(notif); }); + +namespace AsymptoteLsp +{ + using std::unique_ptr; + using std::shared_ptr; + using absyntax::block; + using Level=lsp::Log::Level; + + class SearchPathAddition + { + public: + SearchPathAddition(mem::string const& dir) + { + settings::searchPath.push_back(dir); + } + + SearchPathAddition(SearchPathAddition const&) = delete; + SearchPathAddition& operator=(SearchPathAddition const&) = delete; + + SearchPathAddition(SearchPathAddition&&) = delete; + SearchPathAddition& operator=(SearchPathAddition&&) = delete; + + ~SearchPathAddition() + { + settings::searchPath.pop_back(); + } + }; + +std::string wslDos2Unix(std::string const& dosPath) + { + bool isDrivePath=false; + char drive; + if (dosPath.length() >= 3) + { + if (dosPath[0] == '/' and dosPath[2] == ':') + { + isDrivePath=true; + drive=dosPath[1]; + } + } + + if (isDrivePath) + { + std::stringstream sstream; + sstream << "/mnt/" << (char) tolower(drive) << dosPath.substr(3); + return sstream.str(); + } else + { + return std::string(dosPath); + } + } + +std::string wslUnix2Dos(std::string const& unixPath) + { + bool isMntPath=false; + char drive; + + char actPath[PATH_MAX]; + (void) realpath(unixPath.c_str(), actPath); + std::string fullPath(actPath); + + if (fullPath.length() >= 7) // /mnt/ + { + if (fullPath.find("/mnt/") == 0) + { + isMntPath=true; + drive=fullPath[5]; + } + } + + if (isMntPath) + { + std::stringstream sstream; + sstream << "/" << (char) tolower(drive) << ":" << fullPath.substr(6); + return sstream.str(); + } else + { + return std::string(fullPath); + } + } + + TextDocumentHover::Either fromString(std::string const& str) + { + auto strobj=std::make_pair(boost::make_optional(str), optional()); + std::vector vec{strobj}; + return std::make_pair(vec, nullopt); + } + + TextDocumentHover::Either fromMarkedStr(lsMarkedString const& markedString) + { + auto strobj=std::make_pair((optional) nullopt, make_optional(markedString)); + std::vector vec{strobj}; + return std::make_pair(vec, nullopt); + } + + TextDocumentHover::Either fromMarkedStr(std::vector const& stringList, std::string const& language) + { + std::vector, optional>> vec; + std::transform(stringList.begin(), stringList.end(), std::back_inserter(vec), + [&language](std::string const& str) + { + lsMarkedString lms; + lms.language=language; + lms.value=str; + return std::make_pair((optional) nullopt, make_optional(lms)); + }); + + return std::make_pair(vec, nullopt); + } + + TextDocumentHover::Either fromMarkedStr(std::string const& str, std::string const& language) + { + lsMarkedString lms; + lms.language=language; + lms.value=str; + return fromMarkedStr(lms); + } + + std::string getDocIdentifierRawPath(lsTextDocumentIdentifier const& textDocIdentifier) + { + lsDocumentUri fileUri(textDocIdentifier.uri); + std::string rawPath=settings::getSetting("wsl") ? + wslDos2Unix(fileUri.GetRawPath()) : std::string(fileUri.GetRawPath()); + return static_cast(rawPath); + } + + void AsymptoteLspServer::generateMissingTrees(std::string const& inputFile) + { + using extRefMap=std::unordered_map; + //using extRefMapLoc = std::pair; + std::queue procList; + std::unordered_set processing; + + processing.emplace(inputFile); + SymbolContext* ctx=symmapContextsPtr->at(inputFile).get(); + + for (auto const& locPair : ctx->getEmptyRefs()) + { + procList.emplace(locPair); + } + + // standard BFS algorithm + while (not procList.empty()) + { + auto it=procList.front(); + procList.pop(); + + std::string filename(it->first); + processing.emplace(filename); + + auto mapIt=symmapContextsPtr->find(filename); + if (mapIt != symmapContextsPtr->end()) + { + it->second=mapIt->second.get(); + } + else + { + block* blk=ifile(mem::string(filename.c_str())).getTree(); + auto s=symmapContextsPtr->emplace( + filename, + make_unique(posInFile(1, 1), filename)); + auto fit=std::get<0>(s); + + if(blk == nullptr) + { + // dead end. file cannot be parsed. no new paths. + continue; + } + + blk->createSymMap(fit->second.get()); // parse symbol from there. + // set plain.asy to plain + if (plainCtx != nullptr) + { + fit->second->extRefs.extFileRefs[plainFile]=plainCtx; + } + + // also parse its neighbors + for (auto const& sit : fit->second->getEmptyRefs()) + { + if (processing.find(sit->first) == processing.end()) + { + procList.emplace(sit); + } + else + { + // import cycles detected! + logWarning("Import cycles detected!"); + } + } + it->second=fit->second.get(); + } + } + } + + void LspLog::log(Level level, std::string&& msg) + { + if ((uint32_t)Level::WARNING + settings::verbose >= (uint32_t)level || level == Level::ALL) + { + cerr << msg << std::endl; + } + } + + void LspLog::log(Level level, std::wstring&& msg) + { + if ((uint32_t)Level::WARNING + settings::verbose >= (uint32_t)level || level == Level::ALL) + { + std::wcerr << msg << std::endl; + } + } + + void LspLog::log(Level level, const std::string& msg) + { + if ((uint32_t)Level::WARNING + settings::verbose >= (uint32_t)level || level == Level::ALL) + { + cerr << msg << std::endl; + } + } + + void LspLog::log(Level level, const std::wstring& msg) + { + if ((uint32_t)Level::WARNING + settings::verbose >= (uint32_t)level || level == Level::ALL) + { + std::wcerr << msg << std::endl; + } + } + + AsymptoteLspServer::AsymptoteLspServer( + shared_ptr const& jsonHandler, + shared_ptr const& endpoint, LspLog& log) : + internalREP(make_unique(jsonHandler, endpoint, log)), remoteEndPoint(internalREP.get()), + pjh(jsonHandler), ep(endpoint), _log(log) + { + initializeRequestFn(); + initializeNotifyFn(); + } + + AsymptoteLspServer::AsymptoteLspServer( + RemoteEndPoint* remoteEndPt, + shared_ptr const& jsonHandler, + shared_ptr const& endpoint, LspLog& log) : + internalREP(nullptr), remoteEndPoint(remoteEndPt), + pjh(jsonHandler), ep(endpoint), _log(log) + { + initializeRequestFn(); + initializeNotifyFn(); + } + + void AsymptoteLspServer::initializeRequestFn() + { + REGISTER_REQ_FN(td_initialize, handleInitailizeRequest); + REGISTER_REQ_FN(td_hover, handleHoverRequest); + REGISTER_REQ_FN(td_shutdown, handleShutdownRequest); + REGISTER_REQ_FN(td_definition, handleDefnRequest); + REGISTER_REQ_FN(td_documentColor, handleDocColorRequest); + REGISTER_REQ_FN(td_colorPresentation, handleColorPresRequest); + } + + void AsymptoteLspServer::initializeNotifyFn() + { + REGISTER_NOTIF_FN(Notify_InitializedNotification, onInitialized); + REGISTER_NOTIF_FN(Notify_TextDocumentDidChange, onChange); + REGISTER_NOTIF_FN(Notify_TextDocumentDidOpen, onOpen); + REGISTER_NOTIF_FN(Notify_TextDocumentDidSave, onSave); + REGISTER_NOTIF_FN(Notify_TextDocumentDidClose, onClose); + REGISTER_NOTIF_FN(Notify_Exit, onExit); + } + +//#pragma region notifications + + void AsymptoteLspServer::onInitialized(Notify_InitializedNotification::notify& notify) + { + logInfo("server initalized notification"); + } + + void AsymptoteLspServer::onExit(Notify_Exit::notify& notify) + { + logInfo("server exit notification"); + serverClosed.notify(make_unique(true)); + } + + void AsymptoteLspServer::onChange(Notify_TextDocumentDidChange::notify& notify) + { + logInfo("text change notification"); + + auto& fileChange = notify.params.contentChanges; + if (not fileChange.empty()) + { + bool updatable = true; + block* codeBlk; + try + { + codeBlk=istring(mem::string(fileChange[0].text.c_str())).getTree(); + } + catch (handled_error const&) + { + updatable = false; + } + + if (updatable) + { + std::string rawPath=getDocIdentifierRawPath(notify.params.textDocument.AsTextDocumentIdentifier()); + std::istringstream iss(fileChange[0].text); + updateFileContentsTable(rawPath, iss); + reloadFileRaw(codeBlk, rawPath); + } + } + + logInfo("changed text data"); + } + + void AsymptoteLspServer::onOpen(Notify_TextDocumentDidOpen::notify& notify) + { + logInfo("onOpen notification"); + lsDocumentUri fileUri(notify.params.textDocument.uri); + reloadFile(fileUri.GetRawPath()); + } + + void AsymptoteLspServer::onSave(Notify_TextDocumentDidSave::notify& notify) + { + logInfo("onSave notification"); + lsDocumentUri fileUri(notify.params.textDocument.uri); + reloadFile(fileUri.GetRawPath()); + } + + void AsymptoteLspServer::onClose(Notify_TextDocumentDidClose::notify& notify) + { + logInfo("onClose notification"); + } + +//#pragma endregion + +//#pragma region requests + td_initialize::response AsymptoteLspServer::handleInitailizeRequest(td_initialize::request const& req) + { + clearVariables(); + + symmapContextsPtr=make_unique(); + fileContentsPtr=make_unique< + std::remove_reference::type>(); + plainFile=settings::locateFile("plain", true).c_str(); + plainCtx=reloadFileRaw(plainFile, false); + generateMissingTrees(plainFile); + + td_initialize::response rsp; + rsp.id=req.id; + rsp.result.capabilities.hoverProvider=true; + + lsTextDocumentSyncOptions tdso; + tdso.openClose=true; + tdso.change=lsTextDocumentSyncKind::Full; + lsSaveOptions so; + so.includeText=true; + tdso.save=so; + rsp.result.capabilities.textDocumentSync=opt_right(tdso); + rsp.result.capabilities.definitionProvider=std::make_pair(true, nullopt); + rsp.result.capabilities.colorProvider=std::make_pair(true, nullopt); + return rsp; + } + + SymbolContext* AsymptoteLspServer::fromRawPath(lsTextDocumentIdentifier const& identifier) + { + std::string rawPath=getDocIdentifierRawPath(identifier); + auto fileSymIt=symmapContextsPtr->find(rawPath); + + return fileSymIt != symmapContextsPtr->end() ? fileSymIt->second.get() : nullptr; + } + + td_hover::response AsymptoteLspServer::handleHoverRequest(td_hover::request const& req) + { + td_hover::response rsp; + SymbolContext* fileSymPtr=fromRawPath(req.params.textDocument); + std::vector, optional>> nullVec; + + if (!fileSymPtr) + { + rsp.result.contents.first=nullVec; + return rsp; + } + + auto s=fileSymPtr->searchSymbol(fromLsPosition(req.params.position)); + auto st=std::get<0>(s); + auto ctx=std::get<1>(s); + + if (not st.has_value()) + { + rsp.result.contents.first=nullVec; + return rsp; + } + + auto v=st.value(); + auto symText=std::get<0>(v); + auto startPos=std::get<1>(v); + auto endPos=std::get<2>(v); + + rsp.result.range=make_optional(lsRange(toLsPosition(startPos), toLsPosition(endPos))); + + auto typ=ctx->searchLitSignature(symText); + std::list endResultList; + if (typ.has_value()) + { + endResultList.push_back(typ.value()); + } + endResultList.splice(endResultList.end(), ctx->searchLitFuncSignature(symText)); + + std::vector endResult; + std::copy(endResultList.begin(), endResultList.end(), std::back_inserter(endResult)); + + rsp.result.contents=endResult.empty() ? + fromMarkedStr(" " + symText.name + ";") : + fromMarkedStr(endResult); + return rsp; + } + + td_documentColor::response AsymptoteLspServer::handleDocColorRequest(td_documentColor::request const& req) + { + td_documentColor::response rsp; + + if (SymbolContext* fileSymPtr=fromRawPath(req.params.textDocument)) + { + logInfo("Got Document color request."); + auto& colorsInfo = fileSymPtr->colorInformation; + for (auto const& colorPtr : colorsInfo) + { + ColorInformation cif; + + cif.color = static_cast(*colorPtr); + cif.range.start=toLsPosition(colorPtr->rangeBegin); + + auto s=colorPtr->lastArgPosition; + auto& line=std::get<0>(s); + auto& colm=std::get<1>(s); + size_t offset = 0; + size_t lineOffset = 0; + + auto& strLines = fileContentsPtr->at(getDocIdentifierRawPath(req.params.textDocument)); + char ch=strLines[line + lineOffset + 1][colm - 1 + offset]; + + while ( + ch != ')' and ch != ';' + and line + lineOffset <= strLines.size() + ) + { + ++offset; + if (offset > strLines[line+lineOffset-1].size()) + { + ++lineOffset; + offset = 0; + } + if (line+lineOffset <= strLines.size()) + { + ch=strLines[line + lineOffset - 1][colm - 1 + offset]; + } + } + + if (ch != ')' or line + lineOffset > strLines.size()) + { + continue; + } + + cif.range.end=toLsPosition(make_pair(line+lineOffset, colm+offset+1)); + rsp.result.emplace_back(cif); + } + } + + return rsp; + } + + td_colorPresentation::response AsymptoteLspServer::handleColorPresRequest(td_colorPresentation::request const& req) + { + td_colorPresentation::response rsp; + + if (SymbolContext* fileSymPtr=fromRawPath(req.params.textDocument)) + { + logInfo("Got color presentation request."); + ColorPresentation clp; + + for (auto& colPtr : fileSymPtr->colorInformation) + { + auto& incomingColor = req.params.color; + std::ostringstream ssargs; + std::ostringstream labelargs; + + bool opaque=std::fabs(incomingColor.alpha - 1) < std::numeric_limits::epsilon(); + std::string fnName = opaque ? "rgb" : "rgba"; + + labelargs << std::setprecision(3) << incomingColor.red << "," << incomingColor.green << "," << + incomingColor.blue; + ssargs << incomingColor.red << "," << incomingColor.green << "," << + incomingColor.blue; + if (!opaque) + { + ssargs << "," << incomingColor.alpha; + labelargs << "," << incomingColor.alpha; + } + std::ostringstream ss; + ss << fnName << "(" << ssargs.str() << ")"; + clp.textEdit.newText = ss.str(); + + std::ostringstream lss; + lss << fnName << "(" << labelargs.str() << ")"; + clp.label = lss.str(); + + if (colPtr->rangeBegin == fromLsPosition(req.params.range.start)) + { + clp.textEdit.range = req.params.range; + rsp.result.emplace_back(std::move(clp)); + break; + } + } + } + return rsp; + } + + td_shutdown::response AsymptoteLspServer::handleShutdownRequest(td_shutdown::request const& req) + { + logInfo("got shut down request"); + td_shutdown::response rsp; + JsonNull nullrsp; + lsp::Any anyrsp; + anyrsp.Set(nullrsp); + rsp.result = make_optional(std::move(anyrsp)); + + serverClosed.notify(make_unique(true)); + return rsp; + } + + td_definition::response AsymptoteLspServer::handleDefnRequest(td_definition::request const& req) + { + td_definition::response rsp; + std::list posRanges; + if (SymbolContext* fileSymPtr=fromRawPath(req.params.textDocument)) + { + posInFile pos = fromLsPosition(req.params.position); + auto s=fileSymPtr->searchSymbol(pos); + auto st=std::get<0>(s); + auto ctx=std::get<1>(s); + if (st.has_value()) + { + optional posRange=ctx->searchLitPosition(std::get<0>(st.value()), pos); + if (posRange.has_value()) + { + posRanges.push_back(posRange.value()); + } + + posRanges.splice(posRanges.begin(), ctx->searchLitFuncPositions(std::get<0>(st.value()), pos)); + } + } + rsp.result.first=boost::make_optional(std::vector()); + std::transform( + posRanges.begin(), posRanges.end(), std::back_inserter(rsp.result.first.value()), + [](posRangeInFile const& posRange) + { + auto& fil=std::get<0>(posRange); + auto& posBegin=std::get<1>(posRange); + auto& posEnd=std::get<2>(posRange); + lsRange rng(toLsPosition(posBegin), toLsPosition(posEnd)); + + std::string filReturn( + settings::getSetting("wsl") ? static_cast(wslUnix2Dos(fil)) : fil); + + lsDocumentUri uri(filReturn); + return lsLocation(uri, rng); + }); + return rsp; + } + +//#pragma endregion + void AsymptoteLspServer::reloadFile(std::string const& filename) + { + std::string rawPath=settings::getSetting("wsl") ? wslDos2Unix(filename) : std::string(filename); + reloadFileRaw(static_cast(rawPath)); + } + + void AsymptoteLspServer::updateFileContentsTable(std::string const& filename) + { + std::ifstream fil(filename, std::ifstream::in); + return updateFileContentsTable(filename, fil); + } + + void AsymptoteLspServer::updateFileContentsTable(std::string const& filename, std::istream& in) + { + auto& fileContents = *fileContentsPtr; + fileContents[filename].clear(); + + std::string line; + while (std::getline(in, line)) + { + fileContents[filename].emplace_back(line); + } + } + + SymbolContext* AsymptoteLspServer::reloadFileRaw(block* blk, std::string const& rawPath, bool const& fillTree) + { + if (blk != nullptr) + { + SearchPathAddition sp(stripFile(string(rawPath.c_str()))); + auto it=symmapContextsPtr->find(rawPath); + if (it != symmapContextsPtr->end()) + { + *(it->second)=SymbolContext(posInFile(1, 1), rawPath); + } + else + { + auto s = symmapContextsPtr->emplace( + rawPath, make_unique(posInFile(1, 1), rawPath)); + it=std::get<0>(s); + } + + SymbolContext* newPtr=it->second.get(); + + cerr << rawPath << endl; + + blk->createSymMap(newPtr); + + if (plainCtx != nullptr) + { + it->second->extRefs.extFileRefs[plainFile]=plainCtx; + } + else if (rawPath == plainFile) + { + it->second->extRefs.extFileRefs[plainFile]=newPtr; + } + + if (fillTree) + { + generateMissingTrees(rawPath); + } + return it->second.get(); + } + else + { + return nullptr; + } + } + + SymbolContext* AsymptoteLspServer::reloadFileRaw(std::string const& rawPath, bool const& fillTree) + { + updateFileContentsTable(rawPath); + block* blk=ifile(mem::string(rawPath.c_str())).getTree(); + return reloadFileRaw(blk, rawPath, fillTree); + } + + void AsymptoteLspServer::start() + { + return startIO(cin, cout); + } + + AsymptoteLspServer::~AsymptoteLspServer() + { + } + + void AsymptoteLspServer::startIO(std::istream& in, std::ostream& out) + { + auto inPtr=make_shared(in); + auto outPtr=make_shared(out); + remoteEndPoint->startProcessingMessages(inPtr,outPtr); + serverClosed.wait(); + } + + void AsymptoteLspServer::log(lsp::Log::Level const& level, std::string const& message) + { + _log.log(level, message); + } + + void AsymptoteLspServer::logError(std::string const& message) + { + log(lsp::Log::Level::SEVERE, message); + } + + void AsymptoteLspServer::logWarning(std::string const& message) + { + log(lsp::Log::Level::WARNING, message); + } + + void AsymptoteLspServer::logInfo(std::string const& message) + { + log(lsp::Log::Level::INFO, message); + } + + void AsymptoteLspServer::clearVariables() + { + } + + // TCP Asymptote Server + + TCPAsymptoteLSPServer::TCPAsymptoteLSPServer( + std::string const& addr, std::string const& port, shared_ptr const& jsonHandler, + shared_ptr const& endpoint, LspLog& log) : + lsp::TcpServer(addr, port, jsonHandler, endpoint, log), + AsymptoteLspServer(&point, jsonHandler, endpoint, log) + { + } + + TCPAsymptoteLSPServer::~TCPAsymptoteLSPServer() + { + logInfo("Destroying server..."); + this->stop(); + } + + + void TCPAsymptoteLSPServer::start() + { + std::thread([this]() {this->run();}).detach(); + serverClosed.wait(); + logInfo("Got server closed notification."); + } +} + +#endif -- cgit v1.2.3