forked from OSchip/llvm-project
[mlir][PDLL] Add initial support for a PDLL compilation database
The compilation database acts in a similar way to the compilation database (compile_commands.json) used by clang-tidy, i.e. it provides additional information about the compilation of project files to help the language server. The main piece of information provided by the PDLL compilation database in this commit is the set of include directories used when processing the input .pdll file. This allows for the server to properly process .pdll files that use includes anchored by the include directories set up in the build system. The structure of the textual form of a compilation database is a yaml file containing documents of the following form: ``` --- !FileInfo: filepath: <string> - Absolute file path of the file. includes: <string> - Semi-colon delimited list of include directories. ``` This commit also adds support to cmake for automatically generating a `pdll_compile_commands.yml` file at the top-level of the build directory. Differential Revision: https://reviews.llvm.org/D124076
This commit is contained in:
parent
597fde54a8
commit
fb5a59f6e1
|
@ -7,6 +7,10 @@ function(mlir_tablegen ofn)
|
||||||
PARENT_SCOPE)
|
PARENT_SCOPE)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
# Clear out any pre-existing compile_commands file before processing. This
|
||||||
|
# allows for generating a clean compile_commands on each configure.
|
||||||
|
file(REMOVE ${CMAKE_BINARY_DIR}/pdll_compile_commands.yml)
|
||||||
|
|
||||||
# Declare a PDLL library in the current directory.
|
# Declare a PDLL library in the current directory.
|
||||||
function(add_mlir_pdll_library target inputFile ofn)
|
function(add_mlir_pdll_library target inputFile ofn)
|
||||||
set(LLVM_TARGET_DEFINITIONS ${inputFile})
|
set(LLVM_TARGET_DEFINITIONS ${inputFile})
|
||||||
|
@ -15,6 +19,28 @@ function(add_mlir_pdll_library target inputFile ofn)
|
||||||
set(TABLEGEN_OUTPUT ${TABLEGEN_OUTPUT} ${CMAKE_CURRENT_BINARY_DIR}/${ofn}
|
set(TABLEGEN_OUTPUT ${TABLEGEN_OUTPUT} ${CMAKE_CURRENT_BINARY_DIR}/${ofn}
|
||||||
PARENT_SCOPE)
|
PARENT_SCOPE)
|
||||||
|
|
||||||
|
# Get the current set of include paths for this pdll file.
|
||||||
|
cmake_parse_arguments(ARG "" "" "DEPENDS;EXTRA_INCLUDES" ${ARGN})
|
||||||
|
get_directory_property(tblgen_includes INCLUDE_DIRECTORIES)
|
||||||
|
list(APPEND tblgen_includes ${ARG_EXTRA_INCLUDES})
|
||||||
|
# Filter out any empty include items.
|
||||||
|
list(REMOVE_ITEM tblgen_includes "")
|
||||||
|
|
||||||
|
# Build the absolute path for the current input file.
|
||||||
|
if (IS_ABSOLUTE ${LLVM_TARGET_DEFINITIONS})
|
||||||
|
set(LLVM_TARGET_DEFINITIONS_ABSOLUTE ${inputFile})
|
||||||
|
else()
|
||||||
|
set(LLVM_TARGET_DEFINITIONS_ABSOLUTE ${CMAKE_CURRENT_SOURCE_DIR}/${inputFile})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Append the includes used for this file to the pdll_compilation_commands
|
||||||
|
# file.
|
||||||
|
file(APPEND ${CMAKE_BINARY_DIR}/pdll_compile_commands.yml
|
||||||
|
"--- !FileInfo:\n"
|
||||||
|
" filepath: \"${LLVM_TARGET_DEFINITIONS_ABSOLUTE}\"\n"
|
||||||
|
" includes: \"${CMAKE_CURRENT_SOURCE_DIR};${tblgen_includes}\"\n"
|
||||||
|
)
|
||||||
|
|
||||||
add_public_tablegen_target(${target})
|
add_public_tablegen_target(${target})
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
llvm_add_library(MLIRPdllLspServerLib
|
llvm_add_library(MLIRPdllLspServerLib
|
||||||
|
CompilationDatabase.cpp
|
||||||
LSPServer.cpp
|
LSPServer.cpp
|
||||||
PDLLServer.cpp
|
PDLLServer.cpp
|
||||||
MlirPdllLspServerMain.cpp
|
MlirPdllLspServerMain.cpp
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
//===- CompilationDatabase.cpp - PDLL Compilation Database ----------------===//
|
||||||
|
//
|
||||||
|
// 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 "CompilationDatabase.h"
|
||||||
|
#include "../lsp-server-support/Logging.h"
|
||||||
|
#include "mlir/Support/FileUtilities.h"
|
||||||
|
#include "llvm/ADT/StringRef.h"
|
||||||
|
#include "llvm/Support/YAMLTraits.h"
|
||||||
|
|
||||||
|
using namespace mlir;
|
||||||
|
using namespace mlir::lsp;
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// CompilationDatabase
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(CompilationDatabase::FileInfo)
|
||||||
|
|
||||||
|
namespace llvm {
|
||||||
|
namespace yaml {
|
||||||
|
template <>
|
||||||
|
struct MappingTraits<CompilationDatabase::FileInfo> {
|
||||||
|
static void mapping(IO &io, CompilationDatabase::FileInfo &info) {
|
||||||
|
io.mapRequired("filepath", info.filename);
|
||||||
|
|
||||||
|
// Parse the includes from the yaml stream. These are in the form of a
|
||||||
|
// semi-colon delimited list.
|
||||||
|
std::string combinedIncludes;
|
||||||
|
io.mapRequired("includes", combinedIncludes);
|
||||||
|
for (StringRef include : llvm::split(combinedIncludes, ";")) {
|
||||||
|
if (!include.empty())
|
||||||
|
info.includeDirs.push_back(include.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // end namespace yaml
|
||||||
|
} // end namespace llvm
|
||||||
|
|
||||||
|
CompilationDatabase::CompilationDatabase(ArrayRef<std::string> databases) {
|
||||||
|
for (StringRef filename : databases)
|
||||||
|
loadDatabase(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
const CompilationDatabase::FileInfo *
|
||||||
|
CompilationDatabase::getFileInfo(StringRef filename) const {
|
||||||
|
auto it = files.find(filename);
|
||||||
|
return it == files.end() ? nullptr : &it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompilationDatabase::loadDatabase(StringRef filename) {
|
||||||
|
if (filename.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Set up the input file.
|
||||||
|
std::string errorMessage;
|
||||||
|
std::unique_ptr<llvm::MemoryBuffer> inputFile =
|
||||||
|
openInputFile(filename, &errorMessage);
|
||||||
|
if (!inputFile) {
|
||||||
|
Logger::error("Failed to open compilation database: {0}", errorMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
llvm::yaml::Input yaml(inputFile->getBuffer());
|
||||||
|
|
||||||
|
// Parse the yaml description and add any new files to the database.
|
||||||
|
std::vector<FileInfo> parsedFiles;
|
||||||
|
yaml >> parsedFiles;
|
||||||
|
for (auto &file : parsedFiles) {
|
||||||
|
auto it = files.try_emplace(file.filename, std::move(file));
|
||||||
|
|
||||||
|
// If we encounter a duplicate file, log a warning and ignore it.
|
||||||
|
if (!it.second) {
|
||||||
|
Logger::info("Duplicate .pdll file in compilation database: {0}",
|
||||||
|
file.filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
//===- CompilationDatabase.h - PDLL Compilation Database --------*- 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
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LIB_MLIR_TOOLS_MLIRPDLLSPSERVER_COMPILATIONDATABASE_H_
|
||||||
|
#define LIB_MLIR_TOOLS_MLIRPDLLSPSERVER_COMPILATIONDATABASE_H_
|
||||||
|
|
||||||
|
#include "mlir/Support/LLVM.h"
|
||||||
|
#include "llvm/ADT/StringMap.h"
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace mlir {
|
||||||
|
namespace lsp {
|
||||||
|
/// This class contains a collection of compilation information for files
|
||||||
|
/// provided to the language server, such as the available include directories.
|
||||||
|
/// This database acts as an aggregate in-memory form of compilation databases
|
||||||
|
/// used by the current language client. The textual form of a compilation
|
||||||
|
/// database is a YAML file containing documents of the following form:
|
||||||
|
///
|
||||||
|
/// --- !FileInfo:
|
||||||
|
/// filepath: <string> - Absolute file path of the file.
|
||||||
|
/// includes: <string> - Semi-colon delimited list of include directories.
|
||||||
|
///
|
||||||
|
class CompilationDatabase {
|
||||||
|
public:
|
||||||
|
/// Compilation information for a specific file within the database.
|
||||||
|
struct FileInfo {
|
||||||
|
/// The absolute path to the file.
|
||||||
|
std::string filename;
|
||||||
|
/// The include directories available for the file.
|
||||||
|
std::vector<std::string> includeDirs;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Construct a compilation database from the provided files containing YAML
|
||||||
|
/// descriptions of the database.
|
||||||
|
CompilationDatabase(ArrayRef<std::string> databases);
|
||||||
|
|
||||||
|
/// Get the compilation information for the provided file, or nullptr if the
|
||||||
|
/// database doesn't include information for `filename`.
|
||||||
|
const FileInfo *getFileInfo(StringRef filename) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Load the given database file into this database.
|
||||||
|
void loadDatabase(StringRef filename);
|
||||||
|
|
||||||
|
/// A map of filename to file information for each known file within the
|
||||||
|
/// databases.
|
||||||
|
llvm::StringMap<FileInfo> files;
|
||||||
|
};
|
||||||
|
} // namespace lsp
|
||||||
|
} // namespace mlir
|
||||||
|
|
||||||
|
#endif // LIB_MLIR_TOOLS_MLIRPDLLSPSERVER_COMPILATIONDATABASE_H_
|
|
@ -54,6 +54,10 @@ LogicalResult mlir::MlirPdllLspServerMain(int argc, char **argv) {
|
||||||
llvm::cl::list<std::string> extraIncludeDirs(
|
llvm::cl::list<std::string> extraIncludeDirs(
|
||||||
"pdll-extra-dir", llvm::cl::desc("Extra directory of include files"),
|
"pdll-extra-dir", llvm::cl::desc("Extra directory of include files"),
|
||||||
llvm::cl::value_desc("directory"), llvm::cl::Prefix);
|
llvm::cl::value_desc("directory"), llvm::cl::Prefix);
|
||||||
|
llvm::cl::list<std::string> compilationDatabases(
|
||||||
|
"pdll-compilation-database",
|
||||||
|
llvm::cl::desc("Compilation YAML databases containing additional "
|
||||||
|
"compilation information for .pdll files"));
|
||||||
|
|
||||||
llvm::cl::ParseCommandLineOptions(argc, argv, "PDLL LSP Language Server");
|
llvm::cl::ParseCommandLineOptions(argc, argv, "PDLL LSP Language Server");
|
||||||
|
|
||||||
|
@ -71,7 +75,7 @@ LogicalResult mlir::MlirPdllLspServerMain(int argc, char **argv) {
|
||||||
JSONTransport transport(stdin, llvm::outs(), inputStyle, prettyPrint);
|
JSONTransport transport(stdin, llvm::outs(), inputStyle, prettyPrint);
|
||||||
|
|
||||||
// Configure the servers and start the main language server.
|
// Configure the servers and start the main language server.
|
||||||
PDLLServer::Options options(extraIncludeDirs);
|
PDLLServer::Options options(compilationDatabases, extraIncludeDirs);
|
||||||
PDLLServer server(options);
|
PDLLServer server(options);
|
||||||
return runPdllLSPServer(server, transport);
|
return runPdllLSPServer(server, transport);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include "../lsp-server-support/Logging.h"
|
#include "../lsp-server-support/Logging.h"
|
||||||
#include "../lsp-server-support/Protocol.h"
|
#include "../lsp-server-support/Protocol.h"
|
||||||
|
#include "CompilationDatabase.h"
|
||||||
#include "mlir/Tools/PDLL/AST/Context.h"
|
#include "mlir/Tools/PDLL/AST/Context.h"
|
||||||
#include "mlir/Tools/PDLL/AST/Nodes.h"
|
#include "mlir/Tools/PDLL/AST/Nodes.h"
|
||||||
#include "mlir/Tools/PDLL/AST/Types.h"
|
#include "mlir/Tools/PDLL/AST/Types.h"
|
||||||
|
@ -325,7 +326,7 @@ PDLDocument::PDLDocument(const lsp::URIForFile &uri, StringRef contents,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Properly provide include directories from the client.
|
// Build the set of include directories for this file.
|
||||||
llvm::SmallString<32> uriDirectory(uri.file());
|
llvm::SmallString<32> uriDirectory(uri.file());
|
||||||
llvm::sys::path::remove_filename(uriDirectory);
|
llvm::sys::path::remove_filename(uriDirectory);
|
||||||
includeDirs.push_back(uriDirectory.str().str());
|
includeDirs.push_back(uriDirectory.str().str());
|
||||||
|
@ -1225,11 +1226,16 @@ PDLTextFileChunk &PDLTextFile::getChunkFor(lsp::Position &pos) {
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
struct lsp::PDLLServer::Impl {
|
struct lsp::PDLLServer::Impl {
|
||||||
explicit Impl(const Options &options) : options(options) {}
|
explicit Impl(const Options &options)
|
||||||
|
: options(options), compilationDatabase(options.compilationDatabases) {}
|
||||||
|
|
||||||
/// PDLL LSP options.
|
/// PDLL LSP options.
|
||||||
const Options &options;
|
const Options &options;
|
||||||
|
|
||||||
|
/// The compilation database containing additional information for files
|
||||||
|
/// passed to the server.
|
||||||
|
lsp::CompilationDatabase compilationDatabase;
|
||||||
|
|
||||||
/// The files held by the server, mapped by their URI file name.
|
/// The files held by the server, mapped by their URI file name.
|
||||||
llvm::StringMap<std::unique_ptr<PDLTextFile>> files;
|
llvm::StringMap<std::unique_ptr<PDLTextFile>> files;
|
||||||
};
|
};
|
||||||
|
@ -1245,8 +1251,12 @@ lsp::PDLLServer::~PDLLServer() = default;
|
||||||
void lsp::PDLLServer::addOrUpdateDocument(
|
void lsp::PDLLServer::addOrUpdateDocument(
|
||||||
const URIForFile &uri, StringRef contents, int64_t version,
|
const URIForFile &uri, StringRef contents, int64_t version,
|
||||||
std::vector<Diagnostic> &diagnostics) {
|
std::vector<Diagnostic> &diagnostics) {
|
||||||
|
std::vector<std::string> additionalIncludeDirs = impl->options.extraDirs;
|
||||||
|
if (auto *fileInfo = impl->compilationDatabase.getFileInfo(uri.file()))
|
||||||
|
llvm::append_range(additionalIncludeDirs, fileInfo->includeDirs);
|
||||||
|
|
||||||
impl->files[uri.file()] = std::make_unique<PDLTextFile>(
|
impl->files[uri.file()] = std::make_unique<PDLTextFile>(
|
||||||
uri, contents, version, impl->options.extraDirs, diagnostics);
|
uri, contents, version, additionalIncludeDirs, diagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<int64_t> lsp::PDLLServer::removeDocument(const URIForFile &uri) {
|
Optional<int64_t> lsp::PDLLServer::removeDocument(const URIForFile &uri) {
|
||||||
|
|
|
@ -10,12 +10,14 @@
|
||||||
#define LIB_MLIR_TOOLS_MLIRPDLLSPSERVER_SERVER_H_
|
#define LIB_MLIR_TOOLS_MLIRPDLLSPSERVER_SERVER_H_
|
||||||
|
|
||||||
#include "mlir/Support/LLVM.h"
|
#include "mlir/Support/LLVM.h"
|
||||||
|
#include "llvm/ADT/StringRef.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace mlir {
|
namespace mlir {
|
||||||
namespace lsp {
|
namespace lsp {
|
||||||
struct Diagnostic;
|
struct Diagnostic;
|
||||||
|
class CompilationDatabase;
|
||||||
struct CompletionList;
|
struct CompletionList;
|
||||||
struct DocumentSymbol;
|
struct DocumentSymbol;
|
||||||
struct Hover;
|
struct Hover;
|
||||||
|
@ -30,7 +32,13 @@ class URIForFile;
|
||||||
class PDLLServer {
|
class PDLLServer {
|
||||||
public:
|
public:
|
||||||
struct Options {
|
struct Options {
|
||||||
Options(const std::vector<std::string> &extraDirs) : extraDirs(extraDirs){};
|
Options(const std::vector<std::string> &compilationDatabases,
|
||||||
|
const std::vector<std::string> &extraDirs)
|
||||||
|
: compilationDatabases(compilationDatabases), extraDirs(extraDirs) {}
|
||||||
|
|
||||||
|
/// The filenames for databases containing compilation commands for PDLL
|
||||||
|
/// files passed to the server.
|
||||||
|
const std::vector<std::string> &compilationDatabases;
|
||||||
|
|
||||||
/// Additional list of include directories to search.
|
/// Additional list of include directories to search.
|
||||||
const std::vector<std::string> &extraDirs;
|
const std::vector<std::string> &extraDirs;
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
// RUN: echo -e '--- !FileInfo:\n filepath: "/foo.pdll"\n includes: "%S;%S/../../include"' > %t.yml
|
||||||
|
// RUN: mlir-pdll-lsp-server -pdll-compilation-database=%t.yml -lit-test < %s | FileCheck %s
|
||||||
|
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"pdll","capabilities":{},"trace":"off"}}
|
||||||
|
// -----
|
||||||
|
{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{
|
||||||
|
"uri":"test:///foo.pdll",
|
||||||
|
"languageId":"pdll",
|
||||||
|
"version":1,
|
||||||
|
"text":"#include \"include/included.td\"\n#include \"include/included.pdll\""
|
||||||
|
}}}
|
||||||
|
// Check that we can properly process the includes without errors.
|
||||||
|
// CHECK: "method": "textDocument/publishDiagnostics",
|
||||||
|
// CHECK-NEXT: "params": {
|
||||||
|
// CHECK-NEXT: "diagnostics": [],
|
||||||
|
// CHECK-NEXT: "uri": "test:///foo.pdll",
|
||||||
|
// CHECK-NEXT: "version": 1
|
||||||
|
// CHECK-NEXT: }
|
||||||
|
// -----
|
||||||
|
{"jsonrpc":"2.0","id":7,"method":"shutdown"}
|
||||||
|
// -----
|
||||||
|
{"jsonrpc":"2.0","method":"exit"}
|
|
@ -0,0 +1,2 @@
|
||||||
|
// This file is merely to test the processing of includes, it has
|
||||||
|
// no other purpose or contents.
|
|
@ -0,0 +1,4 @@
|
||||||
|
include "mlir/IR/OpBase.td"
|
||||||
|
|
||||||
|
// This file is merely to test the processing of includes, it has
|
||||||
|
// no other purpose or contents.
|
|
@ -0,0 +1 @@
|
||||||
|
config.excludes = ['include']
|
|
@ -124,6 +124,11 @@
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The file path of the mlir-pdll-lsp-server executable."
|
"description": "The file path of the mlir-pdll-lsp-server executable."
|
||||||
},
|
},
|
||||||
|
"mlir.pdll_compilation_databases": {
|
||||||
|
"scope": "resource",
|
||||||
|
"type": "array",
|
||||||
|
"description": "A list of `pdll_compile_commands.yml` database files containing information about .pdll files processed by the server."
|
||||||
|
},
|
||||||
"mlir.onSettingsChanged": {
|
"mlir.onSettingsChanged": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "prompt",
|
"default": "prompt",
|
||||||
|
|
|
@ -41,41 +41,45 @@ async function promptRestart(settingName: string, promptMessage: string) {
|
||||||
* Activate watchers that track configuration changes for the given workspace
|
* Activate watchers that track configuration changes for the given workspace
|
||||||
* folder, or null if the workspace is top-level.
|
* folder, or null if the workspace is top-level.
|
||||||
*/
|
*/
|
||||||
export async function activate(mlirContext: MLIRContext,
|
export async function activate(
|
||||||
workspaceFolder: vscode.WorkspaceFolder,
|
mlirContext: MLIRContext, workspaceFolder: vscode.WorkspaceFolder,
|
||||||
serverSetting: string, serverPath: string) {
|
serverSettings: string[], serverPaths: string[]) {
|
||||||
// When a configuration change happens, check to see if we should restart the
|
// When a configuration change happens, check to see if we should restart the
|
||||||
// server.
|
// server.
|
||||||
mlirContext.subscriptions.push(vscode.workspace.onDidChangeConfiguration(event => {
|
mlirContext.subscriptions.push(vscode.workspace.onDidChangeConfiguration(event => {
|
||||||
const expandedSetting = `mlir.${serverSetting}`;
|
for (const serverSetting of serverSettings) {
|
||||||
if (event.affectsConfiguration(expandedSetting, workspaceFolder)) {
|
const expandedSetting = `mlir.${serverSetting}`;
|
||||||
promptRestart(
|
if (event.affectsConfiguration(expandedSetting, workspaceFolder)) {
|
||||||
'onSettingsChanged',
|
promptRestart(
|
||||||
`setting '${
|
'onSettingsChanged',
|
||||||
expandedSetting}' has changed. Do you want to reload the server?`);
|
`setting '${
|
||||||
|
expandedSetting}' has changed. Do you want to reload the server?`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// If the server path actually exists, track it in case it changes. Check that
|
// Setup watchers for the provided server paths.
|
||||||
// the path actually exists.
|
|
||||||
if (serverPath === '') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileWatcherConfig = {
|
const fileWatcherConfig = {
|
||||||
disableGlobbing : true,
|
disableGlobbing : true,
|
||||||
followSymlinks : true,
|
followSymlinks : true,
|
||||||
ignoreInitial : true,
|
ignoreInitial : true,
|
||||||
awaitWriteFinish : true,
|
awaitWriteFinish : true,
|
||||||
};
|
};
|
||||||
const fileWatcher = chokidar.watch(serverPath, fileWatcherConfig);
|
for (const serverPath of serverPaths) {
|
||||||
fileWatcher.on('all', (event, _filename, _details) => {
|
if (serverPath === '') {
|
||||||
if (event != 'unlink') {
|
return;
|
||||||
promptRestart(
|
|
||||||
'onSettingsChanged',
|
|
||||||
'MLIR language server binary has changed. Do you want to reload the server?');
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
mlirContext.subscriptions.push(
|
// If the server path actually exists, track it in case it changes.
|
||||||
new vscode.Disposable(() => { fileWatcher.close(); }));
|
const fileWatcher = chokidar.watch(serverPath, fileWatcherConfig);
|
||||||
|
fileWatcher.on('all', (event, _filename, _details) => {
|
||||||
|
if (event != 'unlink') {
|
||||||
|
promptRestart(
|
||||||
|
'onSettingsChanged',
|
||||||
|
'MLIR language server file has changed. Do you want to reload the server?');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mlirContext.subscriptions.push(
|
||||||
|
new vscode.Disposable(() => { fileWatcher.close(); }));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,6 +85,43 @@ export class MLIRContext implements vscode.Disposable {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare the server options for a PDLL server, e.g. populating any
|
||||||
|
* accessible compilation databases.
|
||||||
|
*/
|
||||||
|
async preparePDLLServerOptions(workspaceFolder: vscode.WorkspaceFolder,
|
||||||
|
configsToWatch: string[],
|
||||||
|
pathsToWatch: string[],
|
||||||
|
additionalServerArgs: string[]) {
|
||||||
|
// Process the compilation databases attached for the workspace folder.
|
||||||
|
let databases =
|
||||||
|
config.get<string[]>('pdll_compilation_databases', workspaceFolder);
|
||||||
|
|
||||||
|
// If no databases were explicitly specified, default to a database in the
|
||||||
|
// 'build' directory within the current workspace.
|
||||||
|
if (databases.length === 0) {
|
||||||
|
if (workspaceFolder) {
|
||||||
|
databases.push(workspaceFolder.uri.fsPath +
|
||||||
|
'/build/pdll_compile_commands.yml');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, try to resolve each of the paths.
|
||||||
|
} else {
|
||||||
|
for await (let database of databases) {
|
||||||
|
database = await this.resolvePath(database, '', workspaceFolder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
configsToWatch.push('pdll_compilation_databases');
|
||||||
|
pathsToWatch.push(...databases);
|
||||||
|
|
||||||
|
// Setup the compilation databases as additional arguments to pass to the
|
||||||
|
// server.
|
||||||
|
databases.filter(database => database !== '');
|
||||||
|
additionalServerArgs.push(...databases.map(
|
||||||
|
(database) => `--pdll-compilation-database=${database}`));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activate the language client for the given language in the given workspace
|
* Activate the language client for the given language in the given workspace
|
||||||
* folder.
|
* folder.
|
||||||
|
@ -93,12 +130,27 @@ export class MLIRContext implements vscode.Disposable {
|
||||||
serverSettingName: string, languageName: string,
|
serverSettingName: string, languageName: string,
|
||||||
outputChannel: vscode.OutputChannel):
|
outputChannel: vscode.OutputChannel):
|
||||||
Promise<vscodelc.LanguageClient> {
|
Promise<vscodelc.LanguageClient> {
|
||||||
|
let configsToWatch: string[] = [];
|
||||||
|
let filepathsToWatch: string[] = [];
|
||||||
|
let additionalServerArgs: string[] = [];
|
||||||
|
|
||||||
|
// Initialize additional configurations for this server.
|
||||||
|
if (languageName === 'pdll') {
|
||||||
|
await this.preparePDLLServerOptions(workspaceFolder, configsToWatch,
|
||||||
|
filepathsToWatch,
|
||||||
|
additionalServerArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to activate the language client.
|
||||||
const [server, serverPath] = await this.startLanguageClient(
|
const [server, serverPath] = await this.startLanguageClient(
|
||||||
workspaceFolder, outputChannel, serverSettingName, languageName);
|
workspaceFolder, outputChannel, serverSettingName, languageName,
|
||||||
|
additionalServerArgs);
|
||||||
|
configsToWatch.push(serverSettingName);
|
||||||
|
filepathsToWatch.push(serverPath);
|
||||||
|
|
||||||
// Watch for configuration changes on this folder.
|
// Watch for configuration changes on this folder.
|
||||||
await configWatcher.activate(this, workspaceFolder, serverSettingName,
|
await configWatcher.activate(this, workspaceFolder, configsToWatch,
|
||||||
serverPath);
|
filepathsToWatch);
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +161,8 @@ export class MLIRContext implements vscode.Disposable {
|
||||||
*/
|
*/
|
||||||
async startLanguageClient(workspaceFolder: vscode.WorkspaceFolder,
|
async startLanguageClient(workspaceFolder: vscode.WorkspaceFolder,
|
||||||
outputChannel: vscode.OutputChannel,
|
outputChannel: vscode.OutputChannel,
|
||||||
serverSettingName: string, languageName: string):
|
serverSettingName: string, languageName: string,
|
||||||
|
additionalServerArgs: string[]):
|
||||||
Promise<[ vscodelc.LanguageClient, string ]> {
|
Promise<[ vscodelc.LanguageClient, string ]> {
|
||||||
const clientTitle = languageName.toUpperCase() + ' Language Client';
|
const clientTitle = languageName.toUpperCase() + ' Language Client';
|
||||||
|
|
||||||
|
@ -146,12 +199,12 @@ export class MLIRContext implements vscode.Disposable {
|
||||||
run : {
|
run : {
|
||||||
command : serverPath,
|
command : serverPath,
|
||||||
transport : vscodelc.TransportKind.stdio,
|
transport : vscodelc.TransportKind.stdio,
|
||||||
args : []
|
args : additionalServerArgs
|
||||||
},
|
},
|
||||||
debug : {
|
debug : {
|
||||||
command : serverPath,
|
command : serverPath,
|
||||||
transport : vscodelc.TransportKind.stdio,
|
transport : vscodelc.TransportKind.stdio,
|
||||||
args : []
|
args : additionalServerArgs
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -216,6 +269,45 @@ export class MLIRContext implements vscode.Disposable {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to resolve the given path, or the default path, with an optional
|
||||||
|
* workspace folder. If a path could not be resolved, just returns the
|
||||||
|
* input filePath.
|
||||||
|
*/
|
||||||
|
async resolvePath(filePath: string, defaultPath: string,
|
||||||
|
workspaceFolder: vscode.WorkspaceFolder): Promise<string> {
|
||||||
|
const configPath = filePath;
|
||||||
|
|
||||||
|
// If the path is already fully resolved, there is nothing to do.
|
||||||
|
if (path.isAbsolute(filePath)) {
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a path hasn't been set, try to use the default path.
|
||||||
|
if (filePath === '') {
|
||||||
|
if (defaultPath === '') {
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
filePath = defaultPath;
|
||||||
|
|
||||||
|
// Fallthrough to try resolving the default path.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to resolve the path relative to the workspace.
|
||||||
|
let filePattern: vscode.GlobPattern = '**/' + filePath;
|
||||||
|
if (workspaceFolder) {
|
||||||
|
filePattern = new vscode.RelativePattern(workspaceFolder, filePattern);
|
||||||
|
}
|
||||||
|
let foundUris = await vscode.workspace.findFiles(filePattern, null, 1);
|
||||||
|
if (foundUris.length === 0) {
|
||||||
|
// If we couldn't resolve it, just return the original path anyways. The
|
||||||
|
// file might not exist yet.
|
||||||
|
return configPath;
|
||||||
|
}
|
||||||
|
// Otherwise, return the resolved path.
|
||||||
|
return foundUris[0].fsPath;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to resolve the path for the given server setting, with an optional
|
* Try to resolve the path for the given server setting, with an optional
|
||||||
* workspace folder.
|
* workspace folder.
|
||||||
|
@ -223,37 +315,9 @@ export class MLIRContext implements vscode.Disposable {
|
||||||
async resolveServerPath(serverSettingName: string,
|
async resolveServerPath(serverSettingName: string,
|
||||||
workspaceFolder: vscode.WorkspaceFolder):
|
workspaceFolder: vscode.WorkspaceFolder):
|
||||||
Promise<string> {
|
Promise<string> {
|
||||||
const configServerPath =
|
const serverPath = config.get<string>(serverSettingName, workspaceFolder);
|
||||||
config.get<string>(serverSettingName, workspaceFolder);
|
const defaultPath = MLIRContext.getDefaultServerFilename(serverSettingName);
|
||||||
let serverPath = configServerPath;
|
return this.resolvePath(serverPath, defaultPath, workspaceFolder);
|
||||||
|
|
||||||
// If the path is already fully resolved, there is nothing to do.
|
|
||||||
if (path.isAbsolute(serverPath)) {
|
|
||||||
return serverPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a path hasn't been set, try to use the default path.
|
|
||||||
if (serverPath === '') {
|
|
||||||
serverPath = MLIRContext.getDefaultServerFilename(serverSettingName);
|
|
||||||
if (serverPath === '') {
|
|
||||||
return serverPath;
|
|
||||||
}
|
|
||||||
// Fallthrough to try resolving the default path.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to resolve the path relative to the workspace.
|
|
||||||
let filePattern: vscode.GlobPattern = '**/' + serverPath;
|
|
||||||
if (workspaceFolder) {
|
|
||||||
filePattern = new vscode.RelativePattern(workspaceFolder, filePattern);
|
|
||||||
}
|
|
||||||
let foundUris = await vscode.workspace.findFiles(filePattern, null, 1);
|
|
||||||
if (foundUris.length === 0) {
|
|
||||||
// If we couldn't resolve it, just return the current configuration path
|
|
||||||
// anyways. The file might not exist yet.
|
|
||||||
return configServerPath;
|
|
||||||
}
|
|
||||||
// Otherwise, return the resolved path.
|
|
||||||
return foundUris[0].fsPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
|
|
Loading…
Reference in New Issue