diff --git a/default.nix b/default.nix index 305d0c864..bc2386fae 100644 --- a/default.nix +++ b/default.nix @@ -8,7 +8,6 @@ meson, ninja, nix, - nixpkgs-fmt, pkg-config, nlohmann_json, python312, @@ -29,10 +28,7 @@ stdenv.mkDerivation { pkg-config ]; - nativeCheckInputs = [ - lit - nixpkgs-fmt - ]; + nativeCheckInputs = [ lit ]; buildInputs = [ nix diff --git a/flake.nix b/flake.nix index 2fcfe2020..66a478326 100644 --- a/flake.nix +++ b/flake.nix @@ -12,105 +12,112 @@ }; }; - outputs = { nixpkgs, flake-parts, treefmt-nix, ... }@inputs: flake-parts.lib.mkFlake { inherit inputs; } { - imports = [ - inputs.flake-parts.flakeModules.easyOverlay - inputs.flake-root.flakeModule - ]; - perSystem = { config, pkgs, ... }: - let - inherit (pkgs) nixVersions llvmPackages_16 callPackage stdenv; - nix = nixVersions.nix_2_19; - llvmPackages = llvmPackages_16; - nixf = callPackage ./libnixf { }; - nixt = callPackage ./libnixt { - inherit nix; - }; - nixd = callPackage ./nixd { - inherit nix nixf nixt; - inherit llvmPackages; - }; - nixdMono = callPackage ./. { - inherit nix llvmPackages; - }; - nixdLLVM = nixdMono.override { - stdenv = if stdenv.isDarwin then stdenv else llvmPackages.stdenv; - }; - regressionDeps = with pkgs; [ - clang-tools - nixpkgs-fmt - lit - ]; - shellOverride = old: { - nativeBuildInputs = old.nativeBuildInputs ++ regressionDeps; - shellHook = '' - export PATH="${pkgs.clang-tools}/bin:$PATH" - export NIX_SRC=${nix.src} - export NIX_PATH=nixpkgs=${nixpkgs} - ''; - hardeningDisable = [ "fortify" ]; - }; - treefmtEval = treefmt-nix.lib.evalModule pkgs ./treefmt.nix; - in - { - packages.default = nixd; - overlayAttrs = { - inherit (config.packages) nixd; - }; - packages = { inherit nixd nixf nixt; }; + outputs = + { + nixpkgs, + flake-parts, + treefmt-nix, + ... + }@inputs: + flake-parts.lib.mkFlake { inherit inputs; } { + imports = [ + inputs.flake-parts.flakeModules.easyOverlay + inputs.flake-root.flakeModule + ]; + perSystem = + { config, pkgs, ... }: + let + inherit (pkgs) + nixVersions + llvmPackages_16 + callPackage + stdenv + ; + nix = nixVersions.nix_2_19; + llvmPackages = llvmPackages_16; + nixf = callPackage ./libnixf { }; + nixt = callPackage ./libnixt { inherit nix; }; + nixd = callPackage ./nixd { + inherit nix nixf nixt; + inherit llvmPackages; + }; + nixdMono = callPackage ./. { inherit nix llvmPackages; }; + nixdLLVM = nixdMono.override { stdenv = if stdenv.isDarwin then stdenv else llvmPackages.stdenv; }; + regressionDeps = with pkgs; [ + clang-tools + lit + nixfmt-rfc-style + ]; + shellOverride = old: { + nativeBuildInputs = old.nativeBuildInputs ++ regressionDeps; + shellHook = '' + export PATH="${pkgs.clang-tools}/bin:$PATH" + export NIX_SRC=${nix.src} + export NIX_PATH=nixpkgs=${nixpkgs} + ''; + hardeningDisable = [ "fortify" ]; + }; + treefmtEval = treefmt-nix.lib.evalModule pkgs ./treefmt.nix; + in + { + packages.default = nixd; + overlayAttrs = { + inherit (config.packages) nixd; + }; + packages = { + inherit nixd nixf nixt; + }; - devShells.llvm = nixdLLVM.overrideAttrs shellOverride; + devShells.llvm = nixdLLVM.overrideAttrs shellOverride; - devShells.default = nixdMono.overrideAttrs shellOverride; + devShells.default = nixdMono.overrideAttrs shellOverride; - devShells.nvim = pkgs.mkShell { - nativeBuildInputs = [ - nixd - pkgs.nixpkgs-fmt - pkgs.git - (import ./nixd/docs/editors/nvim-lsp.nix { inherit pkgs; }) - ]; - inputsFrom = [ - config.flake-root.devShell - ]; - shellHook = '' - echo -e "\n\033[1;31mDuring the first time nixd launches, the flake inputs will be fetched from the internet, this is rather slow.\033[0m" - echo -e "\033[1;34mEntering the nvim test environment...\033[0m" - cd $FLAKE_ROOT - export GIT_REPO=https://github.com/nix-community/nixd.git - export EXAMPLES_PATH=nixd/docs/examples - export WORK_TEMP=/tmp/NixOS_Home-Manager - if [ -d "$WORK_TEMP" ]; then - rm -rf $WORK_TEMP - fi - mkdir -p $WORK_TEMP - cp -r $EXAMPLES_PATH/NixOS_Home-Manager/* $WORK_TEMP/ 2>/dev/null - if [[ $? -ne 0 ]]; then - export GIT_DIR=$WORK_TEMP/.git - export GIT_WORK_TREE=/tmp/NixOS_Home-Manager - git init - git config core.sparseCheckout true - git remote add origin $GIT_REPO - echo "$EXAMPLES_PATH/NixOS_Home-Manager/" >$GIT_DIR/info/sparse-checkout - git pull origin main - cp $GIT_WORK_TREE\/$EXAMPLES_PATH/NixOS_Home-Manager/* $GIT_WORK_TREE 2>/dev/null - rm -rf $GIT_WORK_TREE/nixd - fi - cd $WORK_TEMP - echo -e "\033[1;32mNow, you can edit the nix file by running the following command:\033[0m" - echo -e "\033[1;33m'nvim-lsp flake.nix'\033[0m" - echo -e "\033[1;34mEnvironment setup complete.\033[0m" - ''; - }; - devShells.vscodium = pkgs.mkShell { - nativeBuildInputs = [ - nixd - pkgs.nixpkgs-fmt - (import ./nixd/docs/editors/vscodium.nix { inherit pkgs; }) - ]; + devShells.nvim = pkgs.mkShell { + nativeBuildInputs = [ + nixd + pkgs.nixfmt-rfc-style + pkgs.git + (import ./nixd/docs/editors/nvim-lsp.nix { inherit pkgs; }) + ]; + inputsFrom = [ config.flake-root.devShell ]; + shellHook = '' + echo -e "\n\033[1;31mDuring the first time nixd launches, the flake inputs will be fetched from the internet, this is rather slow.\033[0m" + echo -e "\033[1;34mEntering the nvim test environment...\033[0m" + cd $FLAKE_ROOT + export GIT_REPO=https://github.com/nix-community/nixd.git + export EXAMPLES_PATH=nixd/docs/examples + export WORK_TEMP=/tmp/NixOS_Home-Manager + if [ -d "$WORK_TEMP" ]; then + rm -rf $WORK_TEMP + fi + mkdir -p $WORK_TEMP + cp -r $EXAMPLES_PATH/NixOS_Home-Manager/* $WORK_TEMP/ 2>/dev/null + if [[ $? -ne 0 ]]; then + export GIT_DIR=$WORK_TEMP/.git + export GIT_WORK_TREE=/tmp/NixOS_Home-Manager + git init + git config core.sparseCheckout true + git remote add origin $GIT_REPO + echo "$EXAMPLES_PATH/NixOS_Home-Manager/" >$GIT_DIR/info/sparse-checkout + git pull origin main + cp $GIT_WORK_TREE\/$EXAMPLES_PATH/NixOS_Home-Manager/* $GIT_WORK_TREE 2>/dev/null + rm -rf $GIT_WORK_TREE/nixd + fi + cd $WORK_TEMP + echo -e "\033[1;32mNow, you can edit the nix file by running the following command:\033[0m" + echo -e "\033[1;33m'nvim-lsp flake.nix'\033[0m" + echo -e "\033[1;34mEnvironment setup complete.\033[0m" + ''; + }; + devShells.vscodium = pkgs.mkShell { + nativeBuildInputs = [ + nixd + pkgs.nixfmt-rfc-style + (import ./nixd/docs/editors/vscodium.nix { inherit pkgs; }) + ]; + }; + formatter = treefmtEval.config.build.wrapper; }; - formatter = treefmtEval.config.build.wrapper; - }; - systems = nixpkgs.lib.systems.flakeExposed; - }; + systems = nixpkgs.lib.systems.flakeExposed; + }; } diff --git a/libnixf/default.nix b/libnixf/default.nix index cba9bdefb..6be77a1ac 100644 --- a/libnixf/default.nix +++ b/libnixf/default.nix @@ -5,7 +5,6 @@ ninja, pkg-config, lit, - nixpkgs-fmt, gtest, boost182, nlohmann_json, @@ -37,10 +36,7 @@ stdenv.mkDerivation { python312 ]; - nativeCheckInputs = [ - lit - nixpkgs-fmt - ]; + nativeCheckInputs = [ lit ]; buildInputs = [ gtest diff --git a/libnixf/include/nixf/Basic/JSONDiagnostic.h b/libnixf/include/nixf/Basic/JSONDiagnostic.h index 5d0f5299c..de25c6ef7 100644 --- a/libnixf/include/nixf/Basic/JSONDiagnostic.h +++ b/libnixf/include/nixf/Basic/JSONDiagnostic.h @@ -1,5 +1,6 @@ /// \file /// \brief Provide jsonified diagnostic, for other languages/structured output. +#pragma once #include "Diagnostic.h" #include "nixf/Basic/Range.h" diff --git a/libnixf/include/nixf/Basic/NoteKinds.inc b/libnixf/include/nixf/Basic/NoteKinds.inc index 247d6a830..9ef46eefb 100644 --- a/libnixf/include/nixf/Basic/NoteKinds.inc +++ b/libnixf/include/nixf/Basic/NoteKinds.inc @@ -10,7 +10,5 @@ DIAG_NOTE("merge-diff-rec-consider", RecConsider, "will be considered as {}recursive") DIAG_NOTE("note-bcomment-begin", BCommentBegin, "/* comment begins at here") DIAG_NOTE("to-match-this", ToMachThis, "to match this {}") -DIAG_NOTE("var-bind-to-this", VarBindToThis, - "variable will bind to this definition") -DIAG_NOTE("escaping-this-with", EscapingWith, "escaping this with expression") + #endif // DIAG_NOTE diff --git a/libnixf/src/Basic/diagnostic.py b/libnixf/src/Basic/diagnostic.py index c0a9e6710..c55e11795 100644 --- a/libnixf/src/Basic/diagnostic.py +++ b/libnixf/src/Basic/diagnostic.py @@ -219,10 +219,4 @@ class Diagnostic(TypedDict): "severity": "Warning", "message": "unused `with` expression", }, - { - "sname": "sema-escaping-with", - "cname": "EscapingWith", - "severity": "Hint", - "message": "this variable comes from the scope outside of the `with` expression", - }, ] diff --git a/libnixf/src/Sema/VariableLookup.cpp b/libnixf/src/Sema/VariableLookup.cpp index 9b3b38ab0..dfe045ef1 100644 --- a/libnixf/src/Sema/VariableLookup.cpp +++ b/libnixf/src/Sema/VariableLookup.cpp @@ -97,22 +97,6 @@ void VariableLookupAnalysis::lookupVar(const ExprVar &Var, if (Def) { Def->usedBy(Var); Results.insert({&Var, LookupResult{LookupResultKind::Defined, Def}}); - - if (EnclosedWith && !Def->isBuiltin()) { - // Escaping from "with" to outer scope. - // https://github.com/NixOS/nix/issues/490 - - assert(WithEnv && "EnclosedWith -> WithEnv"); - // Make a diagnostic. - Diagnostic &D = - Diags.emplace_back(Diagnostic::DK_EscapingWith, Var.range()); - if (Def->syntax()) { - D.note(Note::NK_VarBindToThis, Def->syntax()->range()); - } - const auto &KwWith = - static_cast(WithEnv->syntax())->kwWith(); - D.note(Note::NK_EscapingWith, KwWith.range()); - } return; } if (EnclosedWith) { diff --git a/libnixf/test/Sema/VariableLookup.cpp b/libnixf/test/Sema/VariableLookup.cpp index b48a78784..89e596bd5 100644 --- a/libnixf/test/Sema/VariableLookup.cpp +++ b/libnixf/test/Sema/VariableLookup.cpp @@ -273,35 +273,6 @@ TEST_F(VLATest, FormalDef) { ASSERT_EQ(Diags.size(), 0); } -TEST_F(VLATest, EscapingWith) { - std::shared_ptr AST = parse("a: with { a = 1; b = 2; }; a + b", Diags); - VariableLookupAnalysis VLA(Diags); - VLA.runOnAST(*AST); - - ASSERT_EQ(Diags.size(), 1); - - const Diagnostic &D = Diags[0]; - - ASSERT_EQ(D.notes().size(), 2); - - ASSERT_EQ(D.notes()[0].kind(), Note::NK_VarBindToThis); - ASSERT_EQ(D.notes()[0].range().lCur().offset(), 0); - ASSERT_EQ(D.notes()[0].range().rCur().offset(), 1); - - ASSERT_EQ(D.notes()[1].kind(), Note::NK_EscapingWith); - ASSERT_EQ(D.notes()[1].range().lCur().offset(), 3); - ASSERT_EQ(D.notes()[1].range().rCur().offset(), 7); -} - -TEST_F(VLATest, EscapingWithButBuiltin) { - std::shared_ptr AST = - parse("with { a = 1; }; [ a true false null ]", Diags); - VariableLookupAnalysis VLA(Diags); - VLA.runOnAST(*AST); - - ASSERT_EQ(Diags.size(), 0); -} - TEST_F(VLATest, InheritRec) { // Make sure inheirt (expr), the expression is binded to "NewEnv". std::shared_ptr AST = diff --git a/nixd/docs/configuration.md b/nixd/docs/configuration.md index 9def02e25..f13866b94 100644 --- a/nixd/docs/configuration.md +++ b/nixd/docs/configuration.md @@ -59,7 +59,7 @@ For vscode users you should write `settings.json`[^settings] like this: "nixd": { "formatting": { // This is the default if ommited. - "command": [ "nixpkgs-fmt" ] + "command": [ "nixfmt" ] }, "options": { // By default, this entriy will be read from `import { }` @@ -94,7 +94,7 @@ nvim_lsp.nixd.setup({ expr = "import { }", }, formatting = { - command = { "nixpkgs-fmt" }, + command = { "nixfmt" }, }, options = { nixos = { @@ -130,7 +130,7 @@ nvim_lsp.nixd.setup({ }, "formatting": { // Which command you would like to do formatting - "command": [ "nixpkgs-fmt" ] + "command": [ "nixfmt" ] }, // Tell the language server your desired option set, for completion // This is lazily evaluated. @@ -149,7 +149,7 @@ nvim_lsp.nixd.setup({ // Control the diagnostic system "diagnostic": { "suppress": [ - "sema-escaping-with" + "sema-extra-with" ] } } @@ -167,7 +167,7 @@ prefer to suppress diagnostics altogether. This can be achieved by utilizing the "diagnostic": { // A list of diagnostic short names "suppress": [ - "sema-escaping-with" + "sema-extra-with" ] } } diff --git a/nixd/docs/editors/nvim-lsp.nix b/nixd/docs/editors/nvim-lsp.nix index 2dac06c33..b2d0e2f57 100644 --- a/nixd/docs/editors/nvim-lsp.nix +++ b/nixd/docs/editors/nvim-lsp.nix @@ -1,4 +1,6 @@ -{ pkgs ? import { } }: +{ + pkgs ? import { }, +}: let neovim = pkgs.neovim.override { configure = { @@ -9,9 +11,7 @@ let ''; packages.myPlugins.start = with pkgs.vimPlugins; [ - (nvim-treesitter.withPlugins (parsers: [ - parsers.nix - ])) + (nvim-treesitter.withPlugins (parsers: [ parsers.nix ])) friendly-snippets luasnip nvim-cmp @@ -107,7 +107,7 @@ let globalstatus = true, }, }) - + ---------------------- -- About bufferline -- ---------------------- @@ -368,7 +368,7 @@ let expr = "import { }", }, formatting = { - command = { "nixpkgs-fmt" }, + command = { "nixfmt" }, }, options = { nixos = { @@ -401,33 +401,33 @@ let }, }) vim.cmd([[ colorscheme nord ]]) - + local keymap = vim.keymap.set - + -- Lsp finder -- Find the symbol definition, implementation, reference. -- If there is no implementation, it will hide. -- When you use action in finder like open, vsplit, then you can use to jump back. keymap("n", "gh", "Lspsaga lsp_finder", { silent = true, desc = "Lsp finder" }) - + -- Code action keymap("n", "ca", "Lspsaga code_action", { silent = true, desc = "Code action" }) keymap("v", "ca", "Lspsaga code_action", { silent = true, desc = "Code action" }) - + -- Rename keymap("n", "gr", "Lspsaga rename", { silent = true, desc = "Rename" }) -- Rename word in whole project keymap("n", "gr", "Lspsaga rename ++project", { silent = true, desc = "Rename in project" }) - + -- Peek definition keymap("n", "gD", "Lspsaga peek_definition", { silent = true, desc = "Peek definition" }) - + -- Go to definition keymap("n", "gd", "Lspsaga goto_definition", { silent = true, desc = "Go to definition" }) - + -- Show line diagnostics keymap("n", "sl", "Lspsaga show_line_diagnostics", { silent = true, desc = "Show line diagnostics" }) - + -- Show cursor diagnostics keymap( "n", @@ -435,43 +435,42 @@ let "Lspsaga show_cursor_diagnostics", { silent = true, desc = "Show cursor diagnostic" } ) - + -- Show buffer diagnostics keymap("n", "sb", "Lspsaga show_buf_diagnostics", { silent = true, desc = "Show buffer diagnostic" }) - + -- Diagnostic jump prev keymap("n", "[e", "Lspsaga diagnostic_jump_prev", { silent = true, desc = "Diagnostic jump prev" }) - + -- Diagnostic jump next keymap("n", "]e", "Lspsaga diagnostic_jump_next", { silent = true, desc = "Diagnostic jump next" }) - + -- Goto prev error keymap("n", "[E", function() require("lspsaga.diagnostic"):goto_prev({ severity = vim.diagnostic.severity.ERROR }) end, { silent = true, desc = "Goto prev error" }) - + -- Goto next error keymap("n", "]E", function() require("lspsaga.diagnostic"):goto_next({ severity = vim.diagnostic.severity.ERROR }) end, { silent = true, desc = "Goto next error" }) - + -- Toggle outline keymap("n", "ss", "Lspsaga outline", { silent = true, desc = "Toggle outline" }) - + -- Hover doc keymap("n", "K", "Lspsaga hover_doc ++keep", { silent = true, desc = "Hover doc" }) - + -- Incoming calls keymap("n", "ci", "Lspsaga incoming_calls", { silent = true, desc = "Incoming calls" }) - + -- Outgoing calls keymap("n", "co", "Lspsaga outgoing_calls", { silent = true, desc = "Outgoing calls" }) - + -- Float terminal keymap("n", "", "Lspsaga term_toggle", { silent = true, desc = "Float terminal" }) keymap("t", "", "Lspsaga term_toggle", { silent = true, desc = "Float terminal" }) ''; - in pkgs.runCommand "nvim-lsp" { } '' mkdir -p $out/bin diff --git a/nixd/docs/nixd-schema.json b/nixd/docs/nixd-schema.json index 9a3751d75..c1a91ffbf 100644 --- a/nixd/docs/nixd-schema.json +++ b/nixd/docs/nixd-schema.json @@ -43,7 +43,7 @@ "formatting": { "command": { "description": "Which command you would like to do formatting", - "default": "nixpkgs-fmt", + "default": "nixfmt", "type": "string" } }, diff --git a/nixd/include/nixd/CommandLine/Configuration.h b/nixd/include/nixd/CommandLine/Configuration.h new file mode 100644 index 000000000..f1904d2d0 --- /dev/null +++ b/nixd/include/nixd/CommandLine/Configuration.h @@ -0,0 +1,14 @@ +/// \file +/// \brief Allow default configuration being passed via CLI +#pragma once + +#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/CommandLine/Options.h b/nixd/include/nixd/CommandLine/Options.h index d5867502d..4252f2faf 100644 --- a/nixd/include/nixd/CommandLine/Options.h +++ b/nixd/include/nixd/CommandLine/Options.h @@ -1,3 +1,5 @@ +#pragma once + #include namespace nixd { diff --git a/nixd/include/nixd/Controller/Configuration.h b/nixd/include/nixd/Controller/Configuration.h index 48b8f5be4..36d5c653a 100644 --- a/nixd/include/nixd/Controller/Configuration.h +++ b/nixd/include/nixd/Controller/Configuration.h @@ -13,7 +13,7 @@ namespace nixd { // NOLINTBEGIN(readability-identifier-naming) struct Configuration { struct Formatting { - std::vector command = {"nixpkgs-fmt"}; + std::vector command = {"nixfmt"}; } formatting; struct OptionProvider { diff --git a/nixd/include/nixd/Controller/Controller.h b/nixd/include/nixd/Controller/Controller.h index 1fb4e36ea..354aca6ed 100644 --- a/nixd/include/nixd/Controller/Controller.h +++ b/nixd/include/nixd/Controller/Controller.h @@ -211,7 +211,7 @@ class Controller : public lspserver::LSPServer { /// Determine whether or not this diagnostic is suppressed. bool isSuppressed(nixf::Diagnostic::DiagnosticKind Kind); void publishDiagnostics(lspserver::PathRef File, - std::optional Version, + std::optional Version, std::string_view Src, const std::vector &Diagnostics); void onRename(const lspserver::RenameParams &Params, diff --git a/nixd/include/nixd/Controller/NixTU.h b/nixd/include/nixd/Controller/NixTU.h index 3c0bb7e83..8a4a84a39 100644 --- a/nixd/include/nixd/Controller/NixTU.h +++ b/nixd/include/nixd/Controller/NixTU.h @@ -22,13 +22,15 @@ class NixTU { std::optional ASTByteCode; std::unique_ptr VLA; std::unique_ptr PMA; + std::shared_ptr Src; public: NixTU() = default; NixTU(std::vector Diagnostics, std::shared_ptr AST, std::optional ASTByteCode, - std::unique_ptr VLA); + std::unique_ptr VLA, + std::shared_ptr Src); [[nodiscard]] const std::vector &diagnostics() const { return Diagnostics; @@ -43,6 +45,8 @@ class NixTU { [[nodiscard]] const nixf::VariableLookupAnalysis *variableLookup() const { return VLA.get(); } + + [[nodiscard]] std::string_view src() const { return *Src; } }; } // 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..54b3edc81 --- /dev/null +++ b/nixd/include/nixd/Support/JSON.h @@ -0,0 +1,40 @@ +#pragma once + +#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/AST.h b/nixd/lib/Controller/AST.h index 2c6a3152b..3536f8197 100644 --- a/nixd/lib/Controller/AST.h +++ b/nixd/lib/Controller/AST.h @@ -39,6 +39,12 @@ struct NotAnIdiomException : IdiomSelectorException { struct VLAException : std::exception {}; +struct NoLocationForBuiltinVariable : std::exception { + [[nodiscard]] const char *what() const noexcept override { + return "builtins are defined in the interpreter, not in the Nix files"; + } +}; + /// \brief No such variable. struct NoSuchVarException : VLAException { [[nodiscard]] const char *what() const noexcept override { diff --git a/nixd/lib/Controller/CodeAction.cpp b/nixd/lib/Controller/CodeAction.cpp index ee6ba8bcd..7b02fdc7e 100644 --- a/nixd/lib/Controller/CodeAction.cpp +++ b/nixd/lib/Controller/CodeAction.cpp @@ -24,7 +24,7 @@ void Controller::onCodeAction(const lspserver::CodeActionParams &Params, std::vector Actions; Actions.reserve(Diagnostics.size()); for (const nixf::Diagnostic &D : Diagnostics) { - auto DRange = toLSPRange(D.range()); + auto DRange = toLSPRange(TU->src(), D.range()); if (!Range.overlap(DRange)) continue; @@ -34,7 +34,7 @@ void Controller::onCodeAction(const lspserver::CodeActionParams &Params, Edits.reserve(F.edits().size()); for (const nixf::TextEdit &TE : F.edits()) { Edits.emplace_back(TextEdit{ - .range = toLSPRange(TE.oldRange()), + .range = toLSPRange(TU->src(), TE.oldRange()), .newText = std::string(TE.newText()), }); } diff --git a/nixd/lib/Controller/Configuration.cpp b/nixd/lib/Controller/Configuration.cpp index 971ec72c8..3bb28e8a9 100644 --- a/nixd/lib/Controller/Configuration.cpp +++ b/nixd/lib/Controller/Configuration.cpp @@ -84,7 +84,7 @@ void Controller::updateConfig(Configuration NewConfig) { // After all, notify all AST modules the diagnostic set has been updated. std::lock_guard TUsGuard(TUsLock); for (const auto &[File, TU] : TUs) { - publishDiagnostics(File, std::nullopt, TU->diagnostics()); + publishDiagnostics(File, std::nullopt, TU->src(), TU->diagnostics()); } } diff --git a/nixd/lib/Controller/Convert.cpp b/nixd/lib/Controller/Convert.cpp index 8a68164d9..8d969f222 100644 --- a/nixd/lib/Controller/Convert.cpp +++ b/nixd/lib/Controller/Convert.cpp @@ -1,11 +1,14 @@ #include "Convert.h" + #include "nixf/Basic/Diagnostic.h" -using namespace lspserver; +#include "lspserver/SourceCode.h" -namespace nixd { +#include -int getLSPSeverity(nixf::Diagnostic::DiagnosticKind Kind) { +using namespace lspserver; + +int nixd::getLSPSeverity(nixf::Diagnostic::DiagnosticKind Kind) { switch (nixf::Diagnostic::severity(Kind)) { case nixf::Diagnostic::DS_Fatal: case nixf::Diagnostic::DS_Error: @@ -21,25 +24,27 @@ int getLSPSeverity(nixf::Diagnostic::DiagnosticKind Kind) { __builtin_unreachable(); } -lspserver::Position toLSPPosition(const nixf::LexerCursor &P) { - return lspserver::Position{static_cast(P.line()), - static_cast(P.column())}; +lspserver::Position nixd::toLSPPosition(llvm::StringRef Code, + const nixf::LexerCursor &P) { + return lspserver::offsetToPosition(Code, P.offset()); } -nixf::Position toNixfPosition(const lspserver::Position &P) { +nixf::Position nixd::toNixfPosition(const lspserver::Position &P) { return {P.line, P.character}; } -nixf::PositionRange toNixfRange(const lspserver::Range &P) { +nixf::PositionRange nixd::toNixfRange(const lspserver::Range &P) { return {toNixfPosition(P.start), toNixfPosition(P.end)}; } -lspserver::Range toLSPRange(const nixf::LexerCursorRange &R) { - return lspserver::Range{toLSPPosition(R.lCur()), toLSPPosition(R.rCur())}; +lspserver::Range nixd::toLSPRange(llvm::StringRef Code, + const nixf::LexerCursorRange &R) { + return lspserver::Range{toLSPPosition(Code, R.lCur()), + toLSPPosition(Code, R.rCur())}; } llvm::SmallVector -toLSPTags(const std::vector &Tags) { +nixd::toLSPTags(const std::vector &Tags) { llvm::SmallVector Result; Result.reserve(Tags.size()); for (const nixf::DiagnosticTag &Tag : Tags) { @@ -54,5 +59,3 @@ toLSPTags(const std::vector &Tags) { } return Result; } - -} // namespace nixd diff --git a/nixd/lib/Controller/Convert.h b/nixd/lib/Controller/Convert.h index fcb71feec..d203b0154 100644 --- a/nixd/lib/Controller/Convert.h +++ b/nixd/lib/Controller/Convert.h @@ -10,13 +10,15 @@ namespace nixd { -lspserver::Position toLSPPosition(const nixf::LexerCursor &P); +lspserver::Position toLSPPosition(llvm::StringRef Code, + const nixf::LexerCursor &P); nixf::Position toNixfPosition(const lspserver::Position &P); nixf::PositionRange toNixfRange(const lspserver::Range &P); -lspserver::Range toLSPRange(const nixf::LexerCursorRange &R); +lspserver::Range toLSPRange(llvm::StringRef Code, + const nixf::LexerCursorRange &R); int getLSPSeverity(nixf::Diagnostic::DiagnosticKind Kind); diff --git a/nixd/lib/Controller/Definition.cpp b/nixd/lib/Controller/Definition.cpp index 908a58195..95bbebea4 100644 --- a/nixd/lib/Controller/Definition.cpp +++ b/nixd/lib/Controller/Definition.cpp @@ -113,10 +113,14 @@ const Definition &findVarDefinition(const ExprVar &Var, } /// \brief Convert nixf::Definition to lspserver::Location -Location convertToLocation(const Definition &Def, URIForFile URI) { +Location convertToLocation(llvm::StringRef Src, const Definition &Def, + URIForFile URI) { + if (!Def.syntax()) + throw NoLocationForBuiltinVariable(); + assert(Def.syntax()); return Location{ .uri = std::move(URI), - .range = toLSPRange(Def.syntax()->range()), + .range = toLSPRange(Src, Def.syntax()->range()), }; } @@ -280,9 +284,9 @@ Locations defineSelect(const ExprSelect &Sel, const VariableLookupAnalysis &VLA, } Locations defineVarStatic(const ExprVar &Var, const VariableLookupAnalysis &VLA, - const URIForFile &URI) { + const URIForFile &URI, llvm::StringRef Src) { const Definition &Def = findVarDefinition(Var, VLA); - return {convertToLocation(Def, URI)}; + return {convertToLocation(Src, Def, URI)}; } template @@ -291,11 +295,12 @@ std::vector mergeVec(std::vector A, const std::vector &B) { return A; } -Locations defineVar(const ExprVar &Var, const VariableLookupAnalysis &VLA, - const ParentMapAnalysis &PM, AttrSetClient &NixpkgsClient, - const URIForFile &URI) { +llvm::Expected +defineVar(const ExprVar &Var, const VariableLookupAnalysis &VLA, + const ParentMapAnalysis &PM, AttrSetClient &NixpkgsClient, + const URIForFile &URI, llvm::StringRef Src) { try { - Locations StaticLocs = defineVarStatic(Var, VLA, URI); + Locations StaticLocs = defineVarStatic(Var, VLA, URI, Src); // Nixpkgs locations. try { @@ -308,8 +313,9 @@ Locations defineVar(const ExprVar &Var, const VariableLookupAnalysis &VLA, } } catch (std::exception &E) { elog("definition/static: {0}", E.what()); + return Locations{}; } - return {}; + return error("unreachable code! Please submit an issue"); } /// \brief Squash a vector into smaller json variant. @@ -374,12 +380,12 @@ void Controller::onDefinition(const TextDocumentPositionParams &Params, return Reply(squash([&]() -> llvm::Expected { // Special case for inherited names. if (const ExprVar *Var = findInheritVar(N, PM, VLA)) - return defineVar(*Var, VLA, PM, *nixpkgsClient(), URI); + return defineVar(*Var, VLA, PM, *nixpkgsClient(), URI, TU->src()); switch (UpExpr.kind()) { case Node::NK_ExprVar: { const auto &Var = static_cast(UpExpr); - return defineVar(Var, VLA, PM, *nixpkgsClient(), URI); + return defineVar(Var, VLA, PM, *nixpkgsClient(), URI, TU->src()); } case Node::NK_ExprSelect: { const auto &Sel = static_cast(UpExpr); diff --git a/nixd/lib/Controller/Diagnostics.cpp b/nixd/lib/Controller/Diagnostics.cpp index 54cf1f4b7..4557888c8 100644 --- a/nixd/lib/Controller/Diagnostics.cpp +++ b/nixd/lib/Controller/Diagnostics.cpp @@ -40,7 +40,7 @@ bool Controller::isSuppressed(nixf::Diagnostic::DiagnosticKind Kind) { } void Controller::publishDiagnostics( - PathRef File, std::optional Version, + PathRef File, std::optional Version, std::string_view Src, const std::vector &Diagnostics) { std::vector LSPDiags; LSPDiags.reserve(Diagnostics.size()); @@ -67,7 +67,7 @@ void Controller::publishDiagnostics( } Diagnostic &Diag = LSPDiags.emplace_back(Diagnostic{ - .range = toLSPRange(D.range()), + .range = toLSPRange(Src, D.range()), .severity = getLSPSeverity(D.kind()), .code = D.sname(), .source = "nixf", @@ -83,7 +83,7 @@ void Controller::publishDiagnostics( .location = Location{ .uri = URIForFile::canonicalize(File, File), - .range = toLSPRange(N.range()), + .range = toLSPRange(Src, N.range()), }, .message = N.format(), }); @@ -93,7 +93,7 @@ void Controller::publishDiagnostics( for (const nixf::Note &N : Notes) { LSPDiags.emplace_back(Diagnostic{ - .range = toLSPRange(N.range()), + .range = toLSPRange(Src, N.range()), .severity = 4, .code = N.sname(), .source = "nixf", diff --git a/nixd/lib/Controller/DocumentHighlight.cpp b/nixd/lib/Controller/DocumentHighlight.cpp index 5f437350f..7587faaf0 100644 --- a/nixd/lib/Controller/DocumentHighlight.cpp +++ b/nixd/lib/Controller/DocumentHighlight.cpp @@ -25,7 +25,8 @@ namespace { std::vector highlight(const nixf::Node &Desc, const ParentMapAnalysis &PMA, const VariableLookupAnalysis &VLA, - const URIForFile &URI) { + const URIForFile &URI, + llvm::StringRef Src) { // Find "definition" auto Def = findDefinition(Desc, PMA, VLA); @@ -34,14 +35,14 @@ std::vector highlight(const nixf::Node &Desc, for (const auto *Use : Def.uses()) { assert(Use); Highlights.emplace_back(DocumentHighlight{ - .range = toLSPRange(Use->range()), + .range = toLSPRange(Src, Use->range()), .kind = DocumentHighlightKind::Read, }); } if (Def.syntax()) { const Node &Syntax = *Def.syntax(); Highlights.emplace_back(DocumentHighlight{ - .range = toLSPRange(Syntax.range()), + .range = toLSPRange(Src, Syntax.range()), .kind = DocumentHighlightKind::Write, }); } @@ -67,7 +68,7 @@ void Controller::onDocumentHighlight( try { const auto &PM = *TU->parentMap(); const auto &VLA = *TU->variableLookup(); - return Reply(highlight(*Desc, PM, VLA, URI)); + return Reply(highlight(*Desc, PM, VLA, URI, TU->src())); } catch (std::exception &E) { elog("textDocument/documentHighlight failed: {0}", E.what()); return Reply(std::vector{}); diff --git a/nixd/lib/Controller/DocumentLink.cpp b/nixd/lib/Controller/DocumentLink.cpp index 50fdadcd3..43fa3511e 100644 --- a/nixd/lib/Controller/DocumentLink.cpp +++ b/nixd/lib/Controller/DocumentLink.cpp @@ -47,7 +47,7 @@ std::optional resolveExprPath(const std::string &BasePath, } void dfs(const Node *N, const std::string &BasePath, - std::vector &Links) { + std::vector &Links, llvm::StringRef Src) { if (!N) return; @@ -59,7 +59,7 @@ void dfs(const Node *N, const std::string &BasePath, // Provide literal path linking. if (auto Link = resolveExprPath(BasePath, Path.parts().literal())) { Links.emplace_back( - DocumentLink{.range = toLSPRange(N->range()), + DocumentLink{.range = toLSPRange(Src, N->range()), .target = URIForFile::canonicalize(*Link, *Link)}); } } @@ -71,7 +71,7 @@ void dfs(const Node *N, const std::string &BasePath, // Traverse on all children for (const Node *Ch : N->children()) { - dfs(Ch, BasePath, Links); + dfs(Ch, BasePath, Links, Src); } } @@ -86,7 +86,7 @@ void Controller::onDocumentLink( if (std::shared_ptr AST = getAST(*TU, Reply)) [[likely]] { // Traverse the AST, provide the links std::vector Links; - dfs(AST.get(), File, Links); + dfs(AST.get(), File, Links, TU->src()); Reply(std::move(Links)); } } diff --git a/nixd/lib/Controller/DocumentSymbol.cpp b/nixd/lib/Controller/DocumentSymbol.cpp index 915664a66..e0dbd21ad 100644 --- a/nixd/lib/Controller/DocumentSymbol.cpp +++ b/nixd/lib/Controller/DocumentSymbol.cpp @@ -8,6 +8,7 @@ #include "nixd/Controller/Controller.h" #include +#include #include #include #include @@ -27,23 +28,24 @@ std::string getLambdaName(const ExprLambda &Lambda) { return Lambda.arg()->id()->name(); } -lspserver::Range getLambdaSelectionRage(const ExprLambda &Lambda) { +lspserver::Range getLambdaSelectionRage(llvm::StringRef Src, + const ExprLambda &Lambda) { if (!Lambda.arg()) { - return toLSPRange(Lambda.range()); + return toLSPRange(Src, Lambda.range()); } if (!Lambda.arg()->id()) { assert(Lambda.arg()->formals()); - return toLSPRange(Lambda.arg()->formals()->range()); + return toLSPRange(Src, Lambda.arg()->formals()->range()); } - return toLSPRange(Lambda.arg()->id()->range()); + return toLSPRange(Src, Lambda.arg()->id()->range()); } -lspserver::Range getAttrRange(const Attribute &Attr) { - auto LCur = toLSPPosition(Attr.key().lCur()); +lspserver::Range getAttrRange(llvm::StringRef Src, const Attribute &Attr) { + auto LCur = toLSPPosition(Src, Attr.key().lCur()); if (Attr.value()) - return {LCur, toLSPPosition(Attr.value()->rCur())}; - return {LCur, toLSPPosition(Attr.key().rCur())}; + return {LCur, toLSPPosition(Src, Attr.value()->rCur())}; + return {LCur, toLSPPosition(Src, Attr.key().rCur())}; } /// Make variable's entry rich. @@ -78,7 +80,7 @@ void richVar(const ExprVar &Var, DocumentSymbol &Sym, /// Collect document symbol on AST. void collect(const Node *AST, std::vector &Symbols, - const VariableLookupAnalysis &VLA) { + const VariableLookupAnalysis &VLA, llvm::StringRef Src) { if (!AST) return; switch (AST->kind()) { @@ -90,8 +92,8 @@ void collect(const Node *AST, std::vector &Symbols, .detail = "string", .kind = SymbolKind::String, .deprecated = false, - .range = toLSPRange(Str.range()), - .selectionRange = toLSPRange(Str.range()), + .range = toLSPRange(Src, Str.range()), + .selectionRange = toLSPRange(Src, Str.range()), .children = {}, }; Symbols.emplace_back(std::move(Sym)); @@ -104,8 +106,8 @@ void collect(const Node *AST, std::vector &Symbols, .detail = "integer", .kind = SymbolKind::Number, .deprecated = false, - .range = toLSPRange(Int.range()), - .selectionRange = toLSPRange(Int.range()), + .range = toLSPRange(Src, Int.range()), + .selectionRange = toLSPRange(Src, Int.range()), .children = {}, }; Symbols.emplace_back(std::move(Sym)); @@ -118,8 +120,8 @@ void collect(const Node *AST, std::vector &Symbols, .detail = "float", .kind = SymbolKind::Number, .deprecated = false, - .range = toLSPRange(Float.range()), - .selectionRange = toLSPRange(Float.range()), + .range = toLSPRange(Src, Float.range()), + .selectionRange = toLSPRange(Src, Float.range()), .children = {}, }; Symbols.emplace_back(std::move(Sym)); @@ -132,8 +134,8 @@ void collect(const Node *AST, std::vector &Symbols, .detail = "attribute name", .kind = SymbolKind::Property, .deprecated = false, - .range = toLSPRange(AN.range()), - .selectionRange = toLSPRange(AN.range()), + .range = toLSPRange(Src, AN.range()), + .selectionRange = toLSPRange(Src, AN.range()), .children = {}, }; Symbols.emplace_back(std::move(Sym)); @@ -146,8 +148,8 @@ void collect(const Node *AST, std::vector &Symbols, .detail = "identifier", .kind = SymbolKind::Variable, .deprecated = false, - .range = toLSPRange(Var.range()), - .selectionRange = toLSPRange(Var.range()), + .range = toLSPRange(Src, Var.range()), + .selectionRange = toLSPRange(Src, Var.range()), .children = {}, }; richVar(Var, Sym, VLA); @@ -157,14 +159,14 @@ void collect(const Node *AST, std::vector &Symbols, case Node::NK_ExprLambda: { std::vector Children; const auto &Lambda = static_cast(*AST); - collect(Lambda.body(), Children, VLA); + collect(Lambda.body(), Children, VLA, Src); DocumentSymbol Sym{ .name = getLambdaName(Lambda), .detail = "lambda", .kind = SymbolKind::Function, .deprecated = false, - .range = toLSPRange(Lambda.range()), - .selectionRange = getLambdaSelectionRage(Lambda), + .range = toLSPRange(Src, Lambda.range()), + .selectionRange = getLambdaSelectionRage(Src, Lambda), .children = std::move(Children), }; Symbols.emplace_back(std::move(Sym)); @@ -174,15 +176,15 @@ void collect(const Node *AST, std::vector &Symbols, std::vector Children; const auto &List = static_cast(*AST); for (const Node *Ch : AST->children()) - collect(Ch, Children, VLA); + collect(Ch, Children, VLA, Src); DocumentSymbol Sym{ .name = "{anonymous}", .detail = "list", .kind = SymbolKind::Array, .deprecated = false, - .range = toLSPRange(List.range()), - .selectionRange = toLSPRange(List.range()), + .range = toLSPRange(Src, List.range()), + .selectionRange = toLSPRange(Src, List.range()), .children = std::move(Children), }; Symbols.emplace_back(std::move(Sym)); @@ -194,28 +196,28 @@ void collect(const Node *AST, std::vector &Symbols, if (!Attr.value()) continue; std::vector Children; - collect(Attr.value(), Children, VLA); + collect(Attr.value(), Children, VLA, Src); DocumentSymbol Sym{ .name = Name, .detail = "attribute", .kind = SymbolKind::Field, .deprecated = false, - .range = getAttrRange(Attr), - .selectionRange = toLSPRange(Attr.key().range()), + .range = getAttrRange(Src, Attr), + .selectionRange = toLSPRange(Src, Attr.key().range()), .children = std::move(Children), }; Symbols.emplace_back(std::move(Sym)); } for (const nixf::Attribute &Attr : SA.dynamicAttrs()) { std::vector Children; - collect(Attr.value(), Children, VLA); + collect(Attr.value(), Children, VLA, Src); DocumentSymbol Sym{ .name = "${dynamic attribute}", .detail = "attribute", .kind = SymbolKind::Field, .deprecated = false, - .range = getAttrRange(Attr), - .selectionRange = toLSPRange(Attr.key().range()), + .range = getAttrRange(Src, Attr), + .selectionRange = toLSPRange(Src, Attr.key().range()), .children = std::move(Children), }; Symbols.emplace_back(std::move(Sym)); @@ -225,7 +227,7 @@ void collect(const Node *AST, std::vector &Symbols, default: // Trivial dispatch. Treat these symbol as same as this level. for (const Node *Ch : AST->children()) - collect(Ch, Symbols, VLA); + collect(Ch, Symbols, VLA, Src); break; } } @@ -239,7 +241,7 @@ void Controller::onDocumentSymbol(const DocumentSymbolParams &Params, if (std::shared_ptr TU = getTU(URI.file().str(), Reply)) { if (std::shared_ptr AST = getAST(*TU, Reply)) { std::vector Symbols; - collect(AST.get(), Symbols, *TU->variableLookup()); + collect(AST.get(), Symbols, *TU->variableLookup(), TU->src()); Reply(std::move(Symbols)); } } diff --git a/nixd/lib/Controller/FindReferences.cpp b/nixd/lib/Controller/FindReferences.cpp index e3369c5eb..f64f4ded2 100644 --- a/nixd/lib/Controller/FindReferences.cpp +++ b/nixd/lib/Controller/FindReferences.cpp @@ -23,7 +23,8 @@ namespace { std::vector findReferences(const nixf::Node &Desc, const ParentMapAnalysis &PMA, const VariableLookupAnalysis &VLA, - const URIForFile &URI) { + const URIForFile &URI, + llvm::StringRef Src) { // Two steps. // 1. Find some "definition" for this node. @@ -37,7 +38,7 @@ std::vector findReferences(const nixf::Node &Desc, assert(Use); Locations.emplace_back(Location{ .uri = URI, - .range = toLSPRange(Use->range()), + .range = toLSPRange(Src, Use->range()), }); } return Locations; @@ -60,7 +61,7 @@ void Controller::onReferences(const TextDocumentPositionParams &Params, const auto &PM = *TU->parentMap(); const auto &VLA = *TU->variableLookup(); try { - return Reply(findReferences(*Desc, PM, VLA, URI)); + return Reply(findReferences(*Desc, PM, VLA, URI, TU->src())); } catch (std::exception &E) { return Reply(error("references: {0}", E.what())); } diff --git a/nixd/lib/Controller/Hover.cpp b/nixd/lib/Controller/Hover.cpp index cc72f92db..0ffe10f9d 100644 --- a/nixd/lib/Controller/Hover.cpp +++ b/nixd/lib/Controller/Hover.cpp @@ -71,7 +71,8 @@ class NixpkgsHoverProvider { } if (Package.Description) { - OS << "## Description" << "\n\n"; + OS << "## Description" + << "\n\n"; OS << *Package.Description; OS << "\n\n"; @@ -141,7 +142,7 @@ void Controller::onHover(const TextDocumentPositionParams &Params, .kind = MarkupKind::Markdown, .value = std::move(*Doc), }, - .range = toLSPRange(N->range()), + .range = toLSPRange(TU->src(), N->range()), }); return; } @@ -173,7 +174,7 @@ void Controller::onHover(const TextDocumentPositionParams &Params, .kind = MarkupKind::Markdown, .value = std::move(Docs), }, - .range = toLSPRange(N->range()), + .range = toLSPRange(TU->src(), N->range()), }); return; } @@ -189,7 +190,7 @@ void Controller::onHover(const TextDocumentPositionParams &Params, .kind = MarkupKind::Markdown, .value = "`" + Name + "`", }, - .range = toLSPRange(N->range()), + .range = toLSPRange(TU->src(), N->range()), }); } } diff --git a/nixd/lib/Controller/InlayHints.cpp b/nixd/lib/Controller/InlayHints.cpp index 58bab19c3..bd9d36d47 100644 --- a/nixd/lib/Controller/InlayHints.cpp +++ b/nixd/lib/Controller/InlayHints.cpp @@ -20,6 +20,7 @@ #include +#include #include #include @@ -51,13 +52,16 @@ class NixpkgsInlayHintsProvider { return Range->contains(R); } + llvm::StringRef Src; + public: NixpkgsInlayHintsProvider(AttrSetClient &NixpkgsProvider, const VariableLookupAnalysis &VLA, const ParentMapAnalysis &PMA, std::optional Range, - std::vector &Hints) - : NixpkgsProvider(NixpkgsProvider), VLA(VLA), PMA(PMA), Hints(Hints) { + std::vector &Hints, llvm::StringRef Src) + : NixpkgsProvider(NixpkgsProvider), VLA(VLA), PMA(PMA), Hints(Hints), + Src(Src) { if (Range) this->Range = toNixfRange(*Range); } @@ -89,10 +93,10 @@ class NixpkgsInlayHintsProvider { if (const std::optional &Version = R.PackageDesc.Version) { // Construct inlay hints. InlayHint H{ - .position = toLSPPosition(N->rCur()), + .position = toLSPPosition(Src, N->rCur()), .label = ": " + *Version, .kind = InlayHintKind::Designator, - .range = toLSPRange(N->range()), + .range = toLSPRange(Src, N->range()), }; Hints.emplace_back(std::move(H)); } @@ -120,7 +124,8 @@ void Controller::onInlayHint(const InlayHintsParams &Params, // Perform inlay hints computation on the range. std::vector Response; NixpkgsInlayHintsProvider NP(*nixpkgsClient(), *TU->variableLookup(), - *TU->parentMap(), Range, Response); + *TU->parentMap(), Range, Response, + TU->src()); NP.dfs(AST.get()); Reply(std::move(Response)); } 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/Controller/NixTU.cpp b/nixd/lib/Controller/NixTU.cpp index 72e1adefc..8e5324ff7 100644 --- a/nixd/lib/Controller/NixTU.cpp +++ b/nixd/lib/Controller/NixTU.cpp @@ -5,9 +5,12 @@ using namespace nixd; NixTU::NixTU(std::vector Diagnostics, std::shared_ptr AST, std::optional ASTByteCode, - std::unique_ptr VLA) + std::unique_ptr VLA, + std::shared_ptr Src) : Diagnostics(std::move(Diagnostics)), AST(std::move(AST)), - ASTByteCode(std::move(ASTByteCode)), VLA(std::move(VLA)) { + ASTByteCode(std::move(ASTByteCode)), VLA(std::move(VLA)), + Src(std::move(Src)) { + assert(this->Src && "Source code should not be null"); if (this->AST) { PMA = std::make_unique(); PMA->runOnAST(*this->AST); diff --git a/nixd/lib/Controller/Rename.cpp b/nixd/lib/Controller/Rename.cpp index 5e675c1fe..41ba232b8 100644 --- a/nixd/lib/Controller/Rename.cpp +++ b/nixd/lib/Controller/Rename.cpp @@ -36,7 +36,8 @@ struct RenameBuiltinException : RenameException { WorkspaceEdit rename(const nixf::Node &Desc, const std::string &NewText, const ParentMapAnalysis &PMA, - const VariableLookupAnalysis &VLA, const URIForFile &URI) { + const VariableLookupAnalysis &VLA, const URIForFile &URI, + llvm::StringRef Src) { using lspserver::TextEdit; // Find "definition" auto Def = findDefinition(Desc, PMA, VLA); @@ -51,13 +52,13 @@ WorkspaceEdit rename(const nixf::Node &Desc, const std::string &NewText, for (const auto *Use : Def.uses()) { Edits.emplace_back(TextEdit{ - .range = toLSPRange(Use->range()), + .range = toLSPRange(Src, Use->range()), .newText = NewText, }); } Edits.emplace_back(TextEdit{ - .range = toLSPRange(Def.syntax()->range()), + .range = toLSPRange(Src, Def.syntax()->range()), .newText = NewText, }); WorkspaceEdit WE; @@ -83,7 +84,7 @@ void Controller::onRename(const RenameParams &Params, const auto &PM = *TU->parentMap(); const auto &VLA = *TU->variableLookup(); try { - return Reply(rename(*Desc, NewText, PM, VLA, URI)); + return Reply(rename(*Desc, NewText, PM, VLA, URI, TU->src())); } catch (std::exception &E) { return Reply(error(E.what())); } @@ -109,8 +110,8 @@ void Controller::onPrepareRename( const auto &PM = *TU->parentMap(); const auto &VLA = *TU->variableLookup(); try { - WorkspaceEdit WE = rename(*Desc, "", PM, VLA, URI); - return Reply(toLSPRange(Desc->range())); + WorkspaceEdit WE = rename(*Desc, "", PM, VLA, URI, TU->src()); + return Reply(toLSPRange(TU->src(), Desc->range())); } catch (std::exception &E) { return Reply(error(E.what())); } diff --git a/nixd/lib/Controller/SemanticTokens.cpp b/nixd/lib/Controller/SemanticTokens.cpp index b2f0dfe36..a8a090d4c 100644 --- a/nixd/lib/Controller/SemanticTokens.cpp +++ b/nixd/lib/Controller/SemanticTokens.cpp @@ -3,16 +3,20 @@ /// [Semantic Tokens]: /// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_semanticTokens +#include "Convert.h" + #include "nixd/Controller/Controller.h" -#include -#include #include #include #include #include #include +#include + +#include + using namespace nixd; using namespace lspserver; using namespace nixf; @@ -59,15 +63,15 @@ class SemanticTokenBuilder { std::vector Raw; + llvm::StringRef Src; + public: - SemanticTokenBuilder(const VariableLookupAnalysis &VLA) : VLA(VLA) {} + SemanticTokenBuilder(const VariableLookupAnalysis &VLA, llvm::StringRef Src) + : VLA(VLA), Src(Src) {} void addImpl(nixf::LexerCursor Pos, unsigned Length, unsigned TokenType, unsigned TokenModifiers) { - Raw.emplace_back(RawSemanticToken{ - {static_cast(Pos.line()), static_cast(Pos.column())}, - Length, - TokenType, - TokenModifiers}); + auto P = toLSPPosition(Src, Pos); + Raw.emplace_back(RawSemanticToken{P, Length, TokenType, TokenModifiers}); } void add(const Node &N, unsigned TokenType, unsigned TokenModifiers) { @@ -235,7 +239,7 @@ void Controller::onSemanticTokens(const SemanticTokensParams &Params, this]() mutable { if (std::shared_ptr TU = getTU(URI.file().str(), Reply)) { if (std::shared_ptr AST = getAST(*TU, Reply)) { - SemanticTokenBuilder Builder(*TU->variableLookup()); + SemanticTokenBuilder Builder(*TU->variableLookup(), TU->src()); Builder.dfs(AST.get()); Reply(SemanticTokens{.tokens = Builder.finish()}); } diff --git a/nixd/lib/Controller/Support.cpp b/nixd/lib/Controller/Support.cpp index 872d0d54a..f4b44bc9d 100644 --- a/nixd/lib/Controller/Support.cpp +++ b/nixd/lib/Controller/Support.cpp @@ -23,6 +23,7 @@ void Controller::actOnDocumentAdd(PathRef File, std::optional Version) { auto Action = [this, File = std::string(File), Version]() { auto Draft = Store.getDraft(File); + std::shared_ptr Src = Draft->Contents; assert(Draft && "Added document is not in the store?"); std::vector Diagnostics; @@ -31,24 +32,24 @@ void Controller::actOnDocumentAdd(PathRef File, if (!AST) { std::lock_guard G(TUsLock); - publishDiagnostics(File, Version, Diagnostics); + publishDiagnostics(File, Version, *Src, Diagnostics); TUs.insert_or_assign(File, std::make_shared(std::move(Diagnostics), std::move(AST), std::nullopt, - /*VLA=*/nullptr)); + /*VLA=*/nullptr, Src)); return; } auto VLA = std::make_unique(Diagnostics); VLA->runOnAST(*AST); - publishDiagnostics(File, Version, Diagnostics); + publishDiagnostics(File, Version, *Src, Diagnostics); { std::lock_guard G(TUsLock); TUs.insert_or_assign( File, std::make_shared(std::move(Diagnostics), std::move(AST), - std::nullopt, std::move(VLA))); + std::nullopt, std::move(VLA), Src)); return; } }; 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/definition/builtin.md b/nixd/tools/nixd/test/definition/builtin.md new file mode 100644 index 000000000..cccd8ee04 --- /dev/null +++ b/nixd/tools/nixd/test/definition/builtin.md @@ -0,0 +1,67 @@ +# RUN: nixd --lit-test < %s | FileCheck %s + +<-- initialize(0) + +```json +{ + "jsonrpc":"2.0", + "id":0, + "method":"initialize", + "params":{ + "processId":123, + "rootPath":"", + "capabilities":{ + }, + "trace":"off" + } +} +``` + + +<-- textDocument/didOpen + +```json +{ + "jsonrpc":"2.0", + "method":"textDocument/didOpen", + "params":{ + "textDocument":{ + "uri":"file:///basic.nix", + "languageId":"nix", + "version":1, + "text":"builtins" + } + } +} +``` + +<-- textDocument/definition(2) + + +```json +{ + "jsonrpc":"2.0", + "id":2, + "method":"textDocument/definition", + "params":{ + "textDocument":{ + "uri":"file:///basic.nix" + }, + "position":{ + "line": 0, + "character":3 + } + } +} +``` + +``` + CHECK: "id": 2, +CHECK-NEXT: "jsonrpc": "2.0", +CHECK-NEXT: "result": null +``` + + +```json +{"jsonrpc":"2.0","method":"exit"} +``` diff --git a/nixd/tools/nixd/test/format.md b/nixd/tools/nixd/test/format/format.md similarity index 87% rename from nixd/tools/nixd/test/format.md rename to nixd/tools/nixd/test/format/format.md index 9c66183ca..274aa83a4 100644 --- a/nixd/tools/nixd/test/format.md +++ b/nixd/tools/nixd/test/format/format.md @@ -1,4 +1,4 @@ -# RUN: nixd --lit-test < %s | FileCheck %s +# RUN: nixd --lit-test -config='{ "formatting": { "command": ["%S/nixfmt"] } }' < %s | FileCheck %s <-- initialize(0) @@ -54,7 +54,7 @@ ``` ``` -CHECK: "newText": "{ stdenv\n, pkgs\n}:\nlet x = 1; in { y = x; }\n", +CHECK: "newText": "Hello\n", ``` ```json 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" diff --git a/nixd/tools/nixd/test/utf16.md b/nixd/tools/nixd/test/utf16.md new file mode 100644 index 000000000..5e2e54599 --- /dev/null +++ b/nixd/tools/nixd/test/utf16.md @@ -0,0 +1,52 @@ +# RUN: nixd --lit-test < %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:///basic.nix", + "languageId":"nix", + "version":1, + "text":"{ a = 1; 测试文本 }" + } + } +} +``` + +``` + CHECK: "code": "parse-unexpected", +CHECK-NEXT: "message": "unexpected text (fix available)", +CHECK-NEXT: "range": { +CHECK-NEXT: "end": { +CHECK-NEXT: "character": 13, +CHECK-NEXT: "line": 0 +CHECK-NEXT: }, +CHECK-NEXT: "start": { +CHECK-NEXT: "character": 9, +CHECK-NEXT: "line": 0 +CHECK-NEXT: } +CHECK-NEXT: }, +``` + +```json +{"jsonrpc":"2.0","method":"exit"} +``` diff --git a/treefmt.nix b/treefmt.nix index 00a70fc3f..945d7ff97 100644 --- a/treefmt.nix +++ b/treefmt.nix @@ -4,7 +4,7 @@ projectRootFile = "flake.nix"; programs = { clang-format.enable = true; - nixpkgs-fmt.enable = true; + nixfmt.enable = true; black.enable = true; }; }