From 5b5e4a4d1be8176fbb07f6d0a7a0b4a7d84b0415 Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Sat, 10 Aug 2024 17:46:31 +0800 Subject: [PATCH] nixd/Controller: allow setting initial configuration via CLI (#579) Add a CLI flag `-config` used to set initial configuration. For editor clients which does not support workspace configuration, the flag could be used as a fallback. --- nixd/include/nixd/CommandLine/Configuration.h | 13 ++++ nixd/include/nixd/Support/Exception.h | 18 ++++++ nixd/include/nixd/Support/JSON.h | 38 ++++++++++++ nixd/lib/CommandLine/Configuration.cpp | 32 ++++++++++ nixd/lib/Controller/LifeTime.cpp | 9 +++ nixd/lib/Support/JSON.cpp | 10 +++ nixd/lib/meson.build | 2 + nixd/tools/nixd/test/format/format.md | 62 +++++++++++++++++++ nixd/tools/nixd/test/format/nixfmt | 2 + 9 files changed, 186 insertions(+) create mode 100644 nixd/include/nixd/CommandLine/Configuration.h create mode 100644 nixd/include/nixd/Support/Exception.h create mode 100644 nixd/include/nixd/Support/JSON.h create mode 100644 nixd/lib/CommandLine/Configuration.cpp create mode 100644 nixd/lib/Support/JSON.cpp create mode 100644 nixd/tools/nixd/test/format/format.md create mode 100755 nixd/tools/nixd/test/format/nixfmt diff --git a/nixd/include/nixd/CommandLine/Configuration.h b/nixd/include/nixd/CommandLine/Configuration.h new file mode 100644 index 000000000..817445b44 --- /dev/null +++ b/nixd/include/nixd/CommandLine/Configuration.h @@ -0,0 +1,13 @@ +/// \file +/// \brief Allow default configuration being passed via CLI + +#include "nixd/Controller/Configuration.h" + +#include + +namespace nixd { + +/// \brief Parse the CLI flag and initialize the config nixd::DefaultConfig +nixd::Configuration parseCLIConfig(); + +} // namespace nixd diff --git a/nixd/include/nixd/Support/Exception.h b/nixd/include/nixd/Support/Exception.h new file mode 100644 index 000000000..7b014f78b --- /dev/null +++ b/nixd/include/nixd/Support/Exception.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +#include + +namespace nixd { + +class LLVMErrorException : public std::exception { + llvm::Error E; + +public: + LLVMErrorException(llvm::Error E) : E(std::move(E)) {} + + llvm::Error takeError() { return std::move(E); } +}; + +} // namespace nixd diff --git a/nixd/include/nixd/Support/JSON.h b/nixd/include/nixd/Support/JSON.h new file mode 100644 index 000000000..6f3a2404b --- /dev/null +++ b/nixd/include/nixd/Support/JSON.h @@ -0,0 +1,38 @@ +#include "Exception.h" + +#include +#include + +#include + +namespace nixd { + +class JSONParseException : public LLVMErrorException { +public: + JSONParseException(llvm::Error E) : LLVMErrorException(std::move(E)) {} + + [[nodiscard]] const char *what() const noexcept override { + return "JSON result cannot be parsed"; + } +}; + +class JSONSchemaException : public LLVMErrorException { +public: + JSONSchemaException(llvm::Error E) : LLVMErrorException(std::move(E)) {} + + [[nodiscard]] const char *what() const noexcept override { + return "JSON schema mismatch"; + } +}; + +llvm::json::Value parse(llvm::StringRef JSON); + +template T fromJSON(const llvm::json::Value &V) { + llvm::json::Path::Root R; + T Result; + if (!fromJSON(V, Result, R)) + throw JSONSchemaException(R.getError()); + return Result; +} + +} // namespace nixd diff --git a/nixd/lib/CommandLine/Configuration.cpp b/nixd/lib/CommandLine/Configuration.cpp new file mode 100644 index 000000000..c3f38cfa8 --- /dev/null +++ b/nixd/lib/CommandLine/Configuration.cpp @@ -0,0 +1,32 @@ +/// \file +/// \brief This file implements CLI initialized configuration. + +#include "nixd/CommandLine/Configuration.h" +#include "nixd/CommandLine/Options.h" +#include "nixd/Controller/Configuration.h" +#include "nixd/Support/JSON.h" + +#include "lspserver/Logger.h" + +#include +#include + +#include + +using namespace nixd; +using namespace llvm::cl; + +namespace { + +opt DefaultConfigJSON{"config", + desc("JSON-encoded initial configuration"), + init(""), cat(NixdCategory)}; + +} // namespace + +Configuration nixd::parseCLIConfig() { + if (DefaultConfigJSON.empty()) + return {}; + + return nixd::fromJSON(nixd::parse(DefaultConfigJSON)); +} diff --git a/nixd/lib/Controller/LifeTime.cpp b/nixd/lib/Controller/LifeTime.cpp index 5912c79e9..fb693ca1a 100644 --- a/nixd/lib/Controller/LifeTime.cpp +++ b/nixd/lib/Controller/LifeTime.cpp @@ -5,9 +5,11 @@ #include "nixd-config.h" +#include "nixd/CommandLine/Configuration.h" #include "nixd/CommandLine/Options.h" #include "nixd/Controller/Controller.h" #include "nixd/Eval/Launch.h" +#include "nixd/Support/Exception.h" #include "lspserver/Protocol.h" @@ -183,6 +185,13 @@ void Controller:: evalExprWithProgress(*Client, getDefaultNixOSOptionsExpr(), "nixos options"); } + try { + Config = parseCLIConfig(); + } catch (LLVMErrorException &Err) { + lspserver::elog("parse CLI config error: {0}, {1}", Err.what(), + Err.takeError()); + std::exit(-1); + } fetchConfig(); } diff --git a/nixd/lib/Support/JSON.cpp b/nixd/lib/Support/JSON.cpp new file mode 100644 index 000000000..d92db13d6 --- /dev/null +++ b/nixd/lib/Support/JSON.cpp @@ -0,0 +1,10 @@ +#include "nixd/Support/JSON.h" + +#include + +llvm::json::Value nixd::parse(llvm::StringRef JSON) { + llvm::Expected E = llvm::json::parse(JSON); + if (!E) + throw JSONParseException(E.takeError()); + return *E; +} diff --git a/nixd/lib/meson.build b/nixd/lib/meson.build index 6dfc3588e..701850a24 100644 --- a/nixd/lib/meson.build +++ b/nixd/lib/meson.build @@ -4,6 +4,7 @@ libnixd_deps = [ nixd_lsp_server, nixf, llvm, nixt ] libnixd_lib = library( 'nixd', + 'CommandLine/Configuration.cpp', 'CommandLine/Options.cpp', 'Controller/AST.cpp', 'Controller/CodeAction.cpp', @@ -33,6 +34,7 @@ libnixd_lib = library( 'Support/AutoCloseFD.cpp', 'Support/AutoRemoveShm.cpp', 'Support/ForkPiped.cpp', + 'Support/JSON.cpp', 'Support/StreamProc.cpp', dependencies: libnixd_deps, include_directories: libnixd_include, diff --git a/nixd/tools/nixd/test/format/format.md b/nixd/tools/nixd/test/format/format.md new file mode 100644 index 000000000..274aa83a4 --- /dev/null +++ b/nixd/tools/nixd/test/format/format.md @@ -0,0 +1,62 @@ +# RUN: nixd --lit-test -config='{ "formatting": { "command": ["%S/nixfmt"] } }' < %s | FileCheck %s + +<-- initialize(0) + +```json +{ + "jsonrpc":"2.0", + "id":0, + "method":"initialize", + "params":{ + "processId":123, + "rootPath":"", + "capabilities":{ + }, + "trace":"off" + } +} +``` + +```json +{ + "jsonrpc":"2.0", + "method":"textDocument/didOpen", + "params":{ + "textDocument":{ + "uri":"file:///format.nix", + "languageId":"nix", + "version":1, + "text":"{ stdenv,\npkgs}: \n let x=1; in { y = x; }" + } + } +} +``` + +<-- textDocument/formatting + +```json +{ + "jsonrpc": "2.0", + "id": 2, + "method": "textDocument/formatting", + "params": { + "textDocument": { + "uri": "file:///format.nix" + }, + "options": { + "tabSize": 2, + "insertSpaces": true, + "trimTrailingWhitespace": true, + "insertFinalNewline": true + } + } +} +``` + +``` +CHECK: "newText": "Hello\n", +``` + +```json +{"jsonrpc":"2.0","method":"exit"} +``` diff --git a/nixd/tools/nixd/test/format/nixfmt b/nixd/tools/nixd/test/format/nixfmt new file mode 100755 index 000000000..e5d398670 --- /dev/null +++ b/nixd/tools/nixd/test/format/nixfmt @@ -0,0 +1,2 @@ +#!/bin/sh +echo "Hello"