From 80564c1e6833406914c8ddc86884ccebaa7f5c79 Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Sat, 10 Aug 2024 09:20:45 +0800 Subject: [PATCH 1/6] nixd/Controller: allow setting initial configuration via CLI 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 | 11 ++++ nixd/lib/CommandLine/Configuration.cpp | 50 +++++++++++++++ nixd/lib/Controller/LifeTime.cpp | 2 + nixd/lib/meson.build | 1 + nixd/tools/nixd/test/format/format.md | 62 +++++++++++++++++++ nixd/tools/nixd/test/format/nixfmt | 2 + 6 files changed, 128 insertions(+) create mode 100644 nixd/include/nixd/CommandLine/Configuration.h create mode 100644 nixd/lib/CommandLine/Configuration.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..47a1aeb32 --- /dev/null +++ b/nixd/include/nixd/CommandLine/Configuration.h @@ -0,0 +1,11 @@ +/// \file +/// \brief Allow default configuration being passed via CLI + +#include "nixd/Controller/Configuration.h" + +namespace nixd { + +/// \brief Parse the CLI flag and initialize the config nixd::DefaultConfig +nixd::Configuration parseCLIConfig(); + +} // namespace nixd diff --git a/nixd/lib/CommandLine/Configuration.cpp b/nixd/lib/CommandLine/Configuration.cpp new file mode 100644 index 000000000..14c1a5b6a --- /dev/null +++ b/nixd/lib/CommandLine/Configuration.cpp @@ -0,0 +1,50 @@ +/// \file +/// \brief This file implements CLI initialized configuration. + +#include "nixd/CommandLine/Configuration.h" +#include "nixd/CommandLine/Options.h" +#include "nixd/Controller/Configuration.h" + +#include "lspserver/Logger.h" + +#include +#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 {}; + + llvm::Expected V = llvm::json::parse(DefaultConfigJSON); + + if (!V) { + llvm::errs() + << "The JSON string of default configuration cannot be parsed, reason: " + << V.takeError() << "\n"; + std::exit(-1); + } + + llvm::json::Path::Root R; + Configuration C; + if (!fromJSON(*V, C, R)) { + // JSON is valid, but it has incorrect schema + llvm::errs() + << "The JSON string of default configuration has invalid schema: " + << R.getError() << "\n"; + std::exit(-1); + } + return C; +} diff --git a/nixd/lib/Controller/LifeTime.cpp b/nixd/lib/Controller/LifeTime.cpp index 5912c79e9..414c6d1f3 100644 --- a/nixd/lib/Controller/LifeTime.cpp +++ b/nixd/lib/Controller/LifeTime.cpp @@ -5,6 +5,7 @@ #include "nixd-config.h" +#include "nixd/CommandLine/Configuration.h" #include "nixd/CommandLine/Options.h" #include "nixd/Controller/Controller.h" #include "nixd/Eval/Launch.h" @@ -183,6 +184,7 @@ void Controller:: evalExprWithProgress(*Client, getDefaultNixOSOptionsExpr(), "nixos options"); } + Config = parseCLIConfig(); fetchConfig(); } diff --git a/nixd/lib/meson.build b/nixd/lib/meson.build index 6dfc3588e..f1e2ac934 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', 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" From 90fca74bc857f709680d488c9872d70f2c1bdc20 Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Sat, 10 Aug 2024 17:32:44 +0800 Subject: [PATCH 2/6] use exceptions --- nixd/include/nixd/CommandLine/Configuration.h | 2 + nixd/include/nixd/Support/Exception.h | 18 +++++++++ nixd/include/nixd/Support/JSON.h | 38 +++++++++++++++++++ nixd/lib/CommandLine/Configuration.cpp | 21 +--------- nixd/lib/Controller/LifeTime.cpp | 8 +++- nixd/lib/Support/JSON.cpp | 10 +++++ nixd/lib/meson.build | 1 + 7 files changed, 78 insertions(+), 20 deletions(-) create mode 100644 nixd/include/nixd/Support/Exception.h create mode 100644 nixd/include/nixd/Support/JSON.h create mode 100644 nixd/lib/Support/JSON.cpp diff --git a/nixd/include/nixd/CommandLine/Configuration.h b/nixd/include/nixd/CommandLine/Configuration.h index 47a1aeb32..817445b44 100644 --- a/nixd/include/nixd/CommandLine/Configuration.h +++ b/nixd/include/nixd/CommandLine/Configuration.h @@ -3,6 +3,8 @@ #include "nixd/Controller/Configuration.h" +#include + namespace nixd { /// \brief Parse the CLI flag and initialize the config nixd::DefaultConfig 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 index 14c1a5b6a..e361ee4be 100644 --- a/nixd/lib/CommandLine/Configuration.cpp +++ b/nixd/lib/CommandLine/Configuration.cpp @@ -4,6 +4,7 @@ #include "nixd/CommandLine/Configuration.h" #include "nixd/CommandLine/Options.h" #include "nixd/Controller/Configuration.h" +#include "nixd/Support/JSON.h" #include "lspserver/Logger.h" @@ -28,23 +29,5 @@ Configuration nixd::parseCLIConfig() { if (DefaultConfigJSON.empty()) return {}; - llvm::Expected V = llvm::json::parse(DefaultConfigJSON); - - if (!V) { - llvm::errs() - << "The JSON string of default configuration cannot be parsed, reason: " - << V.takeError() << "\n"; - std::exit(-1); - } - - llvm::json::Path::Root R; - Configuration C; - if (!fromJSON(*V, C, R)) { - // JSON is valid, but it has incorrect schema - llvm::errs() - << "The JSON string of default configuration has invalid schema: " - << R.getError() << "\n"; - std::exit(-1); - } - return C; + return nixd::fromJSON(nixd::parse(DefaultConfigJSON)); } diff --git a/nixd/lib/Controller/LifeTime.cpp b/nixd/lib/Controller/LifeTime.cpp index 414c6d1f3..a205fd27e 100644 --- a/nixd/lib/Controller/LifeTime.cpp +++ b/nixd/lib/Controller/LifeTime.cpp @@ -11,6 +11,7 @@ #include "nixd/Eval/Launch.h" #include "lspserver/Protocol.h" +#include "nixd/Support/Exception.h" #include @@ -184,7 +185,12 @@ void Controller:: evalExprWithProgress(*Client, getDefaultNixOSOptionsExpr(), "nixos options"); } - Config = parseCLIConfig(); + try { + Config = parseCLIConfig(); + } catch (LLVMErrorException &Err) { + lspserver::elog("{0}, reason: {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 f1e2ac934..701850a24 100644 --- a/nixd/lib/meson.build +++ b/nixd/lib/meson.build @@ -34,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, From 811ecc486592cd92033a45bb2295e18f784213a1 Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Sat, 10 Aug 2024 17:35:38 +0800 Subject: [PATCH 3/6] Update nixd/lib/Controller/LifeTime.cpp --- nixd/lib/Controller/LifeTime.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nixd/lib/Controller/LifeTime.cpp b/nixd/lib/Controller/LifeTime.cpp index a205fd27e..7304ca805 100644 --- a/nixd/lib/Controller/LifeTime.cpp +++ b/nixd/lib/Controller/LifeTime.cpp @@ -188,7 +188,7 @@ void Controller:: try { Config = parseCLIConfig(); } catch (LLVMErrorException &Err) { - lspserver::elog("{0}, reason: {1}", Err.what(), Err.takeError()); + lspserver::elog("parse CLI config error: {0}, {1}", Err.what(), Err.takeError()); std::exit(-1); } fetchConfig(); From 569b25d1ad7ac5ce6436a1c67914fba164ff91ee Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Sat, 10 Aug 2024 17:37:57 +0800 Subject: [PATCH 4/6] remove iostream --- nixd/lib/CommandLine/Configuration.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/nixd/lib/CommandLine/Configuration.cpp b/nixd/lib/CommandLine/Configuration.cpp index e361ee4be..c3f38cfa8 100644 --- a/nixd/lib/CommandLine/Configuration.cpp +++ b/nixd/lib/CommandLine/Configuration.cpp @@ -11,7 +11,6 @@ #include #include -#include #include using namespace nixd; From 384f4981755c3958366a4046809c666ce1ff42c3 Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Sat, 10 Aug 2024 17:38:27 +0800 Subject: [PATCH 5/6] sort header --- nixd/lib/Controller/LifeTime.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nixd/lib/Controller/LifeTime.cpp b/nixd/lib/Controller/LifeTime.cpp index 7304ca805..4faa9bb1b 100644 --- a/nixd/lib/Controller/LifeTime.cpp +++ b/nixd/lib/Controller/LifeTime.cpp @@ -9,9 +9,9 @@ #include "nixd/CommandLine/Options.h" #include "nixd/Controller/Controller.h" #include "nixd/Eval/Launch.h" +#include "nixd/Support/Exception.h" #include "lspserver/Protocol.h" -#include "nixd/Support/Exception.h" #include From b58c582ddbe248367a90999b3f550c866aed583a Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Sat, 10 Aug 2024 17:40:30 +0800 Subject: [PATCH 6/6] fixup fmt --- nixd/lib/Controller/LifeTime.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nixd/lib/Controller/LifeTime.cpp b/nixd/lib/Controller/LifeTime.cpp index 4faa9bb1b..fb693ca1a 100644 --- a/nixd/lib/Controller/LifeTime.cpp +++ b/nixd/lib/Controller/LifeTime.cpp @@ -188,7 +188,8 @@ void Controller:: try { Config = parseCLIConfig(); } catch (LLVMErrorException &Err) { - lspserver::elog("parse CLI config error: {0}, {1}", Err.what(), Err.takeError()); + lspserver::elog("parse CLI config error: {0}, {1}", Err.what(), + Err.takeError()); std::exit(-1); } fetchConfig();